Prevent HTML5 Drag Ghost Image Flying Back - html

I am building a sortable list that uses the HTML5 drag and drop. It works well, but I have a frustrating problem when it comes to the ghost image. The ghost image always wants to fly back to the location that it came from. The result is that if a list item change positions, the ghost image will fly back to the wrong list item.
Ideally, the ghost image should just not show at all after the onDragEnd event. I've tried setting the image to an empty image on dragEnd with
handleDragEnd(e) {
e.dataTransfer.setDragImage(new Image(0, 0), 0, 0);
...
but I think that you can only use setDragImage in onDragStart.
Is there a way to hide the ghost image onDragEnd, or at the very least get it to fly back to the correct location?

The revert effect is the default behavior on Safari, but you can control this default behavior in the HTML that you control. You only need to add a dragover event on the zone on which you want to prevent this revert effect and call e.preventDefault(). Only limitation: you won't be able to control when user drags the element out of the browser or the frame that you control.
But something like this gets rid of the problem you're describing:
var draggedId;
var img = new Image();
img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAO0lEQVR42u3OMQ0AMAgAsGFiCvCvDQeggoSjVdD4lf0OC0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBwR0DX1VWkeseHpAAAAAASUVORK5CYII=";
document.getElementById('a').ondragstart = dragstart_handler;
document.getElementById('b').ondragstart = dragstart_handler;
document.ondragover = (e) => {
if (draggedId === "a") {
e.preventDefault();
}
}
function dragstart_handler(e) {
draggedId = e.target.id;
e.dataTransfer.setDragImage(img, 0, 0)
}
div {
cursor: pointer;
}
div:hover {
color: blue;
}
<div id=a draggable=true>
This element won't revert on dragend.
</div>
<div id=b draggable=true>
This one will revert.
</div>

Related

AS3 Apache Flex: click randomly not updating stage until mouse moves

I have a SWF that displays a field of stars as a single BITMAP added to the Stage. The image is dynamically generated through the Bitmap's BitmapData, to allow trackball interaction with the star field. Planets and other buttons are also on the stage with hint Labels underneath.
All works as expected. However, sometimes clicking a button, performs an operation, that does not show up until the mouse moves. For example, clicking the button to toggle planet labels, will immediately update the stage with labels turning on and off. But sometimes, the click does nothing, until the mouse is moved, or another click is made and then both clicks activate in series.
Same goes for a button that resets the star field to show either the Sun to Earth or Earth to Sun view. Click the image (button) and the background star field updates immediately. However, sometimes it does nothing until the mouse moves. It seems like an interface timing issue beyond my control.
Anybody encounter this situation? Working on MAC with Firefox, all latest updates.
Here's the event code.
private function moveRespond(e:MouseEvent):void {
var X:int=mouseX,Y:int=mouseY,R:int=Assets.Assets.radius/10; if(R<15){R=15}
if( moveActive ) {
Assets.Assets.STUFF.view.rotateMatrix(PT.x,PT.y,X,Y);
FIELD.update(w,h);
}
else {
Assets.Assets.STUFF.line.hover(X,Y,R);
var btn:Vector.<Button>=Assets.Assets.BTN_list;
var i:int,I:int=btn.length;
for( i=0; i<I; i++ ) {
btn[i].label.visible=btn[i].hover(X,Y);
} }
DP.x=PT.x-X; PT.x=X;
DP.y=PT.y-Y; PT.y=Y;
}
private function upRespond(e:MouseEvent):void { moveActive=false }
private function downRespond(e:MouseEvent):void { moveActive=true;
var X:int=mouseX,Y:int=mouseY;
PT.x=X; PT.y=Y;
}
private function clickRespond(e:MouseEvent):void {
...
//lots of button management, similar to above
//FIELD.update(w,h); and new zodiac sign fetch...
//but if there was a soft error in here,
//then the click at the end would get skipped,
//and it doesnt.
...
if( click ) { Assets.Assets.soundClick(X); }
}
Because of the randomness of the error, I'm assuming a memory thrashing somewhere in my code that is interrupting the next event, but kinda hoping its a communications issue because the missed-click is Event-queued. It just doesnt get sent to my App until either a move or another click. The App.swf can be run without the wrapper (preloader basically), and it has the same randomness. Even though it is very rare, it is still very annoying.
There is also a ConstantUpdate timer:
public function constantUpdate(evt:TimerEvent):void {
Mouse.hide(); Mouse.show();
dateMessage();
if( !Assets.Assets.BTN_help.glowing && Math.random()<0.05 ) { Assets.Assets.BTN_help.glow(); }
if( !Assets.Assets.BTN_logo.glowing && Math.random()<0.01 ) { Assets.Assets.BTN_logo.glow(); }
if( !Assets.Assets.BTN_next.glowing && Math.random()<0.05 ) { Assets.Assets.BTN_next.glow(); }
Assets.Assets.BTN_help.update();
Assets.Assets.BTN_logo.update();
Assets.Assets.BTN_next.update();
Assets.Assets.BTN_star.update();
Assets.Assets.BTN_note.update();
Assets.Assets.BTN_moon.update();
}
Just more button management. The Mouse hide/show pings the Mouse after the contextMenu deactivates. Otherwise, the mouse turned into an arrow over the contextMenu and would stay that way until mouse left the window and returned. Still have some mouse hick-ups on startup though - not setting my cursor after App.swf loaded. Have no idea if that is related to the clicking issue, but here's that code:
var cursor:MouseCursorData=new MouseCursorData();
cursor.hotSpot=new Point(
Assets.Assets.BTN_sun.icon.width/2,
Assets.Assets.BTN_sun.icon.height/2);
var pointer:Vector.<BitmapData>=new <BitmapData>[Assets.Assets.BTN_sun.icon.bitmapData];
cursor.data=pointer;
Mouse.registerCursor("myCursor",cursor);
Mouse.cursor="myCursor";
I dont keep a local handle to the cursor object.

Hiding/Showing stage elements when using drag and drop

I asked a question about drag and drop a few days ago, which was answered here:
Changing text dynamically with drag / drop in Flash CS6 (AS3)
I have another question related to the first question, not sure if I was supposed to ask it on that original question or start a new one.
Anyway, I am trying to have some stage elements show on the screen when the slider is being used, and then hide when the user stops using ("drops") the slider button. The elements are a button and a textarea component.
I set the alpha for each element to zero and then wanted to use as3 to tell the system when to set the alpha to 100, and then of course when to set it back to zero.
The issue I'm having is that when I let go of the slider, everything becomes hidden again EXCEPT for the text inside the textarea component.
Here is my code:
slider.addEventListener(MouseEvent.MOUSE_DOWN, fl_ClickToDrag);
function fl_ClickToDrag(event:MouseEvent):void
{
slider.startDrag(false, rectangle);
graphic.alpha = 1;
textarea1.alpha = 1;
}
stage.addEventListener(Event.ENTER_FRAME, _onEnterFrame);
function _onEnterFrame(e:Event):void {
if (slider.x > 26){
textarea1.text = '25+';
}
}
slider.addEventListener(MouseEvent.MOUSE_UP, fl_ReleaseToDrop);
function fl_ReleaseToDrop(event:MouseEvent):void
{
slider.stopDrag();
graphic.alpha = 0;
textarea1.alpha = 0;
}
What am I missing here?

Kinetic.js don't lose grip on mouse out

When you drag an object and mouse is out of rendering area, dragging stops (firing an event) and user loses a grip.
It's extremelly inconvenient, taking into account that all other technologies (Flash, raw HTML5 Canvas, etc) allows to save the grip even if mouse is out.
Is there a way to solve the problem?
UPDATE: Up to the moment solved the problem by changing library file and binding listeners to the document, not to the container. I know that it's bad to hack into library files, but after inspecting the library's source code I haven't found out way around.
you could check if the element is out of sight and if so bring it back:
shape.on('dragend', function() {
var pos = shape.getPosition();
var layer = pos.getLayer();
if (pos.y < 0) {
pos.y = 0;
}
var maxY = layer.getHeight() - shape.getHeight();
if (pos.y > maxY) {
pos.y = maxY
}
shape.setPosition(pos);
}
Look at element.setCapture(). You can call it from within an event handler for a mouse event, eg. mousedown:
function mouseDown(e) {
e.target.setCapture();
e.target.addEventListener("mousemove", mouseMoved, false);
}
Although browser support is a bit spotty (IE and Firefox support it, not sure about other browsers), for cross browser use you would have to fall back to the binding on the document approach you've already hit upon.

How to trigger custom cursor change on mousedown without mousemove event (jQuery)

Trying to change the cursor
I am trying to make a draggable scrollable image and as such am trying to change the cursor over an image when a variable is set to zoom mode so I can have good usability. What I am finding is that the cursor only changes once I have clicked the ".page" and THEN moved the mouse not just as I click the page as it should. Here is some example code:
$(".page").on("mousedown", function (evt) {
if(model.zoomMode){
$('.page').css('cursor','url("img/hand_closed.gif"),auto');
}
}).on("mouseup", function (evt) {
if(model.zoomMode){
$('.page').css('cursor','url("img/hand_open.gif"),auto');
}
});
Another approach relying more on CSS
This seems to happen when I use classes to achieve the same effect as well. ie. zoom mode adds a class outside of the .page object and then the javascript is:
$(".page").on("mousedown", function (evt) {
$('.page').addClass('mouseDown');
}).on("mouseup", function (evt) {
$('.page').removeClass('mouseDown');
});
Then in the CSS:
.zoom .page:hover{
cursor:url(../img/hand_open.gif),auto;
}
.zoom .page.mouseDown:hover{
cursor:url(../img/hand_closed.gif),auto;
}
I am using chrome 18 to test. Anyone know a way to trigger the CSS cursor being applied without moving the mouse?
Short answer: No.
Long answer: Nope, sorry.
(source)

html5 canvas drawing / saving

I'm attempting to turn the html5 canvas into a slide annotator. My goal is to upload an Image of a slide, allow the user to draw on it, and then save it.
Whats working: I successfully have slides being uploaded as background images and allowing the user to draw on them. I also have the save function working.
Whats wrong: Once the image is save, the drawing the user made stays but the background image doesn't become part of the saved image.
Is there a way to save the canvas to an image and keep the slide background in it? Perhaps its not supposed to be a background image?
Here is some of my code:
The Canvas + button:
<div id="main">
<canvas id="drop1" class="drop" style=" background: url(pdf_images/pdf-save-0.png); background-repeat:no-repeat;">
</canvas>
</div>
<input type="button" id="savepngbtn" value="Save Slide">
Saving the Canvas:
document.getElementById("savepngbtn").onclick = function() {
saveCanvas(oCanvas, "PNG");
}
function saveCanvas(pCanvas, strType) {
var bRes = false;
if (strType == "PNG")
bRes = Canvas2Image.saveAsPNG(oCanvas);
if (!bRes) {
alert("Sorry, this browser is not capable of saving " + strType + " files!");
return false;
}
}
you will need to make the 'background-image' part of the canvas if you would like to save them as a single image.
you can try something like:
var ctx = yourcanvas.getContext('2d');
var img = new Image();
img.src = 'http://yourimage.png';
img.onload = function() {
ctx.drawImage(img, 0, 0)
}
then allow the user to add the annotations and save.
It shouldn't be a background image.
You should use the canvas's provided DrawImage to draw an image. It's pretty simple to do so, just create a new image element (ie. new Image(), set the onload event to call DrawImage with itself as a parameter and then set the .src attribute so that it actually loads).
Obviously this should be the first thing you do as the image is kindof paramount to how the canvas works. You could add an additional hook within onload so as to not allow the user to do any drawing until the image has loaded.