Drag drop issue on Mobile Devices - javascript

Now, I have developed web application using KonvaJS for drag-drop which can be used by mobile devices as well by following the example in W3School.
So far, it works in both desktop mode and mobile device mode using Mozilla Firefox. However, it does not work in mobile device mode using Google Chrome browser. Here is the code in question.
var width = 300; var height = 400;
var stage = new Konva.Stage({
container: 'MyCanvas',
width: width,
height: height
});
var layer = new Konva.Layer();
stage.add(layer);
// draw into dynacmic canvas element
var PaintCanvas = document.createElement('canvas'); // need to perform flood fill or paint by create canvas outside drag drop mechanic
var PaintContext = PaintCanvas.getContext('2d');
var activeBool = false;
var itemURL = '';
var isMobile = false; //initiate as false
// device detection
if(/Android|webOS|iPhone|iPad|Mac|Macintosh|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
isMobile = true;
}
var ImageDirectoryElement = document.getElementById('ImageDirectory'); // the place to show image files in the directory
if(!isMobile)
{
ImageDirectoryElement.addEventListener("dragstart", dragStart, false);
}
else if(isMobile) {
ImageDirectoryElement.addEventListener("touchstart", dragStart, false); // so far, it response to touchstart
}
function dragStart(e)
{
//when the drag starts
itemURL = e.target.src;
if (e.target !== stage) { //if user is dragging circle
activeBool = true; //the drag is active
}
}
var con = stage.container();
if(!isMobile)
{
con.addEventListener("dragover", drag, false);
} else if(isMobile) {
con.addEventListener("touchmove", drag, false); // however, it failed to response to touchmove
}
function drag(e)
{
if (activeBool) { //if the drag is active
e.preventDefault(); //the user cant do anything else but drag
}
}
if(!isMobile)
{
con.addEventListener("drop", dragEnd, false);
} else if(isMobile) {
con.addEventListener("touchend", dragEnd, false);
}
function dragEnd(e)
{
e.preventDefault();
stage.setPointersPositions(e);
var ImageDragDrop = new Image();
PaintCanvas.width = ImageDragDrop.width;
PaintCanvas.height = ImageDragDrop.height;
var ImageCanvasWidth = ImageDragDrop.width;
var ImageCanvasHeight = ImageDragDrop.height;
var ImageCanvasWidthHeightRatio = ImageCanvasWidth/ImageCanvasHeight;
PaintContext.fillStyle = BrushColorString;
PaintContext.strokeStyle = BrushColorString;
PaintContext.lineJoin = "round";
PaintContext.lineCap = "round";
PaintContext.lineWidth = BrushRadius;
PaintContext.shadowBlur = BrushRadius;
PaintContext.shadowColor = BrushColorString;
PaintContext.drawImage(ImageDragDrop, 0, 0);
var image = new Konva.Image({
name: 'FaceImg',
src: itemURL,
id: 'Image' + ImageNumber,
image: PaintCanvas,
draggable: false, // may need to make automatic scaling
});
ImageID = ImageID + ImageNumber;
ImageNumber1 = ImageNumber;
ImageNumber = ImageNumber+1;
ImageID = 'Image';
image.cache();
image.filters([Konva.Filters.Contrast,Konva.Filters.Brighten]);
// the rest are the way to define the top-left position along with the sizes of
// Konva.Image element to maker automatic positioning.
....
}
If you could come up with solution to deal with drag drop behaviors in different browsers, please show me your resolution that really work though.

Related

Is it possible to block the smartphone from opening gallery?

I have an input for capturing image from the smartphone camera like this below:
<input ng-model="fileModel" type="file" name="input_default" accept="image/*;capture=camera"
capture="camera" capture />
If there is no camera on the installed smartphone(OS Android or IOS), the input opens the cell phone gallery. I wanted the user to be alerted that it doesn't have a camera and to not open the device gallery.
I tried this way below, but it didn't work.
var el = document.createElement('input');
if(el.capture == undefined){
// display message user
} else{
// open the camera normally on the device
}
Is it possible to BLOCK the smartphone from opening gallery using Javascript?
This is a working example of how to take photo from camera directly with WebRTC by opening camera and taking photo.
This approach is used by many govt website to validate authenticity.
This is the demo,
https://yari-demos.prod.mdn.mozit.cloud/en-US/docs/Web/API/Media_Streams_API/Taking_still_photos/_sample_.demo.html
And this is complete explanation,
https://developer.mozilla.org/en-US/docs/Web/API/Media_Streams_API/Taking_still_photos
(function() {
// The width and height of the captured photo. We will set the
// width to the value defined here, but the height will be
// calculated based on the aspect ratio of the input stream.
var width = 320; // We will scale the photo width to this
var height = 0; // This will be computed based on the input stream
// |streaming| indicates whether or not we're currently streaming
// video from the camera. Obviously, we start at false.
var streaming = false;
// The various HTML elements we need to configure or control. These
// will be set by the startup() function.
var video = null;
var canvas = null;
var photo = null;
var startbutton = null;
function showViewLiveResultButton() {
if (window.self !== window.top) {
// Ensure that if our document is in a frame, we get the user
// to first open it in its own tab or window. Otherwise, it
// won't be able to request permission for camera access.
document.querySelector(".contentarea").remove();
const button = document.createElement("button");
button.textContent = "View live result of the example code above";
document.body.append(button);
button.addEventListener('click', () => window.open(location.href));
return true;
}
return false;
}
function startup() {
if (showViewLiveResultButton()) { return; }
video = document.getElementById('video');
canvas = document.getElementById('canvas');
photo = document.getElementById('photo');
startbutton = document.getElementById('startbutton');
navigator.mediaDevices.getUserMedia({video: true, audio: false})
.then(function(stream) {
video.srcObject = stream;
video.play();
})
.catch(function(err) {
console.log("An error occurred: " + err);
});
video.addEventListener('canplay', function(ev){
if (!streaming) {
height = video.videoHeight / (video.videoWidth/width);
// Firefox currently has a bug where the height can't be read from
// the video, so we will make assumptions if this happens.
if (isNaN(height)) {
height = width / (4/3);
}
video.setAttribute('width', width);
video.setAttribute('height', height);
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
streaming = true;
}
}, false);
startbutton.addEventListener('click', function(ev){
takepicture();
ev.preventDefault();
}, false);
clearphoto();
}
// Fill the photo with an indication that none has been
// captured.
function clearphoto() {
var context = canvas.getContext('2d');
context.fillStyle = "#AAA";
context.fillRect(0, 0, canvas.width, canvas.height);
var data = canvas.toDataURL('image/png');
photo.setAttribute('src', data);
}
// Capture a photo by fetching the current contents of the video
// and drawing it into a canvas, then converting that to a PNG
// format data URL. By drawing it on an offscreen canvas and then
// drawing that to the screen, we can change its size and/or apply
// other changes before drawing it.
function takepicture() {
var context = canvas.getContext('2d');
if (width && height) {
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
var data = canvas.toDataURL('image/png');
photo.setAttribute('src', data);
} else {
clearphoto();
}
}
// Set up our event listener to run the startup process
// once loading is complete.
window.addEventListener('load', startup, false);
})();

Mouse position is only read on the first frame

i have been having trouble with reading a mouse position on a canvas. The code is working (semi) correctly as it reads the position when clicking he canvas in IE but only on one frame, in chrome it is just displaying the value as 0.
Here is the full code:
<script>
var blip = new Audio("blip.mp3");
blip.load();
var levelUp = new Audio("levelUp.mp3");
levelUp.load();
var canvas = document.getElementById('game');
var context = canvas.getContext('2d');
context.font = '18pt Calibri';
context.fillStyle = 'white';
//load and draw background image
var bgReady = false;
var background = new Image();
background.src = 'images/background.jpg';
background.onload = function(){
bgReady = true;
}
var startMessage = 'Click the canvas to start';
//load plane image
var planeReady = false;
var planeImage = new Image();
planeImage.src = 'images/plane.png';
planeImage.onload = function() {
planeReady = true;
}
//load missile image
var missileReady = false;
var missileImage = new Image();
missileImage.src = 'images/missile-flipped.gif';
missileImage.onload = function() {
missileReady = true;
}
//initialise lives and score
var score = 0;
var lives = 3;
var missilesLaunched = 0;
var missileSpeed = 5;
var level = 1;
var missileX = 960;
var missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
function getMousePos(canvas, event) {
return {
x: input.x - rect.left,
y: input.y - rect.top
};
}
function update_images(event) {
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
planeImage.y = pos.y;
missileX = missileX - missileSpeed;
if (missileX < - 70) {
missilesLaunched++;
missileX = 960;
missileY = Math.random() * 500;
if (missileY > 480) {
missileY = 480;
}
blip.play();
score = missilesLaunched;
if (score % 5 == 0) {
missileSpeed = missileSpeed + 2;
level++;
levelUp.play();
}
}
}
function reload_images() {
if (bgReady = true) {
context.drawImage(background, 0, 0);
}
if (planeReady = true) {
context.drawImage(planeImage, 10, planeImage.y);
}
if (missileReady = true) {
context.drawImage(missileImage, missileX, missileY);
}
context.fillText('Lives: ' + lives, 200, 30);
context.fillText('Score: ' + score, 650, 30);
context.fillText('Level: ' + missileSpeed, 420, 30);
context.fillText('Position: ' + missileImage.y, 420, 70);
}
function main(event) {
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
update_images(event);
reload_images();
if (lives > 0) {
window.requestAnimationFrame(main);
}
else {
}
}
function start() {
context.drawImage(background, 0, 0);
context.fillText('Click the canvas to start', 350, 250);
function startMain(event) {
game.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("mousedown", startMain);
}
start();
</script>
Joe, you should actually be capturing the mouse position every time you click...
...but you're actually also starting a new game (without stopping the old one), every time you click, too.
First problem: starting game engine several times to draw on the same instance of the canvas
Solution:
In your start function, you need to remove the mousedown event listener, after you've triggered it.
function start () {
// ... other setup
function startMain (event) {
canvas.removeEventListener("click", startMain);
main(event);
}
canvas.addEventListener("click", startMain);
}
Now it will only listen for the first click, before starting, and will only start once.
Second Problem: mouse doesn't update as expected
Solution: two issues here...
...first, you are passing event into main on first call...
...after that, you're passing main into requestAnimationFrame.
requestAnimationFrame won't call it with an event, it will call it with the number of microseconds (or ms or some other unit as a fractional precision of ms) since the page was loaded.
So the first time you got main({ type: "mousedown", ... });.
The next time you get main(4378.002358007);
So lets refactor the startMain we had above, so that main never ever collects an event, just a time.
function startMain ( ) {
canvas.removeEventListener("click", startMain);
requestAnimationFrame(main);
}
The next problem is that even if you were getting just events, you're only ever capturing a click event (which as we mentioned earlier, fires a new copy of the game logic).
Your solution is to separate the code which catches mouse events from the code which reads mouse position.
var mouseInput = { x: 0, y: 0 };
document.addEventListener("mousemove", function (event) {
mouseInput.x = event.clientX;
mouseInput.y = event.clientY;
});
function getMousePos (rect, input) {
return {
x : input.x - rect.left,
y : input.y - rect.top
};
}
// currently in updateImages (should not be there, but... a different story)
var pos = getMousePos(canvas.getBoundingClientRect(), mouseInput);
You've got other problems, too...
You're calling getMousePos and passing in game at the moment. I don't see where game is defined in your JS, so either you're making game somewhere else (begging for bugs), or it's undefined, and your app blows up right there.
You should really be building this with your console / dev-tools open, in a hands-on fashion, and cleaning bugs in each section, as you go.

Fabricjs canvas reset after zooming

I have a fabricjs canvas that I need to be able to zoom in and out and also change the image/object inside several times.
For this I setup the canvas in the first time the page loads like this:
fabric.Object.prototype.hasBorders = false;
fabric.Object.prototype.hasControls = false;
canvas = new fabric.Canvas('my_canvas', {renderOnAddRemove: false, stateful: false});
canvas.defaultCursor = "pointer";
canvas.backgroundImageStretch = false;
canvas.selection = false;
canvas.clear();
var image = document.getElementById('my_image');
if (image != null) {
imageSrc = image.src;
if(imageSrc.length > 0){
fabric.Image.fromURL(imageSrc, function(img) {
img = scaleImage(canvas, img); //shrinks the image to fit the canvas
img.selectable = false;
canvas.centerObject(img);
canvas.setActiveObject(img);
canvas.add(img);
});
}
}
canvas.deactivateAll().renderAll();
Then when I need to change the image/object in the canvas or when the page reloads, I try to reset the canvas like this:
canvas.clear();
canvas.remove(canvas.getActiveObject());
var image = document.getElementById('my_image');
if (image != null) {
imageSrc = image.src;
if(imageSrc.length > 0){
fabric.Image.fromURL(imageSrc, function(img) {
img = scaleImage(canvas, img); //shrinks the image to fit the canvas
img.selectable = false;
canvas.centerObject(img);
canvas.setActiveObject(img);
canvas.add(img);
});
}
}
Not sure if it matters but the way I change the image is by changing the source in 'my_image' and reseting the canvas with the above method.
This works well until I use canvas.zoomToPoint, as per this thread, after this, the image/object starts changing position when I reset the zoom or click the canvas with the mouse while it is zoomed, seeming to jump at each change in the top left corner direction, eventually disappearing from view.
Reset Zoom:
canvas.setZoom(1);
resetCanvas(); //(above method)
How can I restore the image/object position?
I tried doing the initial setup instead of the reset and seamed to work visually but was in fact adding a new layer of upper canvas at each new setup so it is no good.
Is there a way to reset the canvas to original state without causing this behavior and still be able to zoom in/out correctly?
Although this question is very old, here is what I did using the current version of fabric.js 2.2.4:
canvas.setViewportTransform([1,0,0,1,0,0]);
For your information: zooming to a point is a recalculation of the viewport transformation. The upper matrix is this is the initial viewport transform matrix.
I eventually fixed the problems I was having.
To reset the zoom, instead of just setting the zoom to 1 with canvas.setZoom(1), I reapplied the canvas.zoomToPoint method to the same point but with zoom 1, to force the initial zoom but regarding the same point that was used to zoom in.
As for the problem of restoring the image position in canvas (after panning for instance) it is as simple as removing the image, centering it in the canvas and re-adding it to the canvas as was done when adding first time:
var img = canvas.getActiveObject();
canvas.remove(img);
canvas.centerObject(img);
canvas.setActiveObject(img);
canvas.add(img);
canvas.renderAll();
See below snippet - here I do the same - zooming together, but degrouping the objects in case somebody clicks on it.
The problem to get to original object properties can be solved, ungrouping the group and creating copies of them and reattaching - a bit annoying, but the only solution I found.
<script id="main">
// canvas and office background
var mainGroup;
var canvas = this.__canvas = new fabric.Canvas('c');
fabric.Object.prototype.transparentCorners = false;
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
createOnjects(canvas);
// events - zoom
$(canvas.wrapperEl).on('mousewheel', function(e) {
var target = canvas.findTarget(e);
var delta = e.originalEvent.wheelDelta / 5000;
if (target) {
target.scaleX += delta;
target.scaleY += delta;
// constrain
if (target.scaleX < 0.1) {
target.scaleX = 0.1;
target.scaleY = 0.1;
}
// constrain
if (target.scaleX > 10) {
target.scaleX = 10;
target.scaleY = 10;
}
target.setCoords();
canvas.renderAll();
return false;
}
});
// mouse down
canvas.on('mouse:up', function(options) {
if (options.target) {
var thisTarget = options.target;
var mousePos = canvas.getPointer(options.e);
if (thisTarget.isType('group')) {
// unGroup
console.log(mousePos);
var clone = thisTarget._objects.slice(0);
thisTarget._restoreObjectsState();
for (var i = 0; i < thisTarget._objects.length; i++) {
var o = thisTarget._objects[i];
if (o._element.alt == "officeFloor")
continue;
else {
if (mousePos.x >= o.originalLeft - o.currentWidth / 2 && mousePos.x <= o.originalLeft + o.currentWidth / 2
&& mousePos.y >= o.originalTop - o.currentHeight / 2 && mousePos.y <= o.originalTop + o.currentHeight / 2)
console.log(o._element.alt);
}
}
// remove all objects and re-render
canvas.remove(thisTarget);
canvas.clear().renderAll();
var group = new fabric.Group();
for (var i = 0; i < clone.length; i++) {
group.addWithUpdate(clone[i]);
}
canvas.add(group);
canvas.renderAll();
}
}
});
// functions
function createOnjects(canvas) {
// ToDo: jQuery.parseJSON() for config file (or web service)
fabric.Image.fromURL('pics/OfficeFloor.jpg', function(img) {
var back = img.set({ left: 100, top: 100 });
back._element.alt = "officeFloor";
back.hasControls = false;
fabric.Image.fromURL('pics/me.png', function(img) {
var me = img.set({ left: -420, top: 275 });
me._element.alt = "me";
console.log(me);
var group = new fabric.Group([ back, me], { left: 700, top: 400, hasControls: false });
canvas.clear().renderAll();
canvas.add(group);
// remove all objects and re-render
});
});
}
</script>

How to drag and drop between canvases in Fabric.js

I understand Fabric.js have built-in support on drag-n-drop within the same canvas.
How can we make it work for multiple canvas?
Or from an non-canvas html element e.g. an image from a table?
Drag and drop between canvases is possible in Fabric.js, but involves some manipulation of private properties. For this reason it is not guaranteed to be functional with future versions of Fabric.js.
Working demo: https://jsfiddle.net/mmalex/kdbu9f3y/
Video capture: https://youtu.be/nXZgCmIrpqQ
Key features:
✓ can drag and drop between any number of canvases (not only two),
✓ can drag back and forth between canvases without interruption,
✓ can transform (mirror) dropped image without interruption of manipulation.
Step 1 – prepare canvases, load images, arrange everything for demo:
//create two canvases
var canvas0El = document.getElementById("c0");
canvas0El.width = canvas0El.offsetWidth;
canvas0El.height = canvas0El.parentElement.offsetHeight;
var canvas1El = document.getElementById("c1");
canvas1El.width = canvas1El.offsetWidth;
canvas1El.height = canvas1El.parentElement.offsetHeight;
var canvas0 = new fabric.Canvas('c0');
canvas0.setBackgroundColor('rgba(19, 19, 19, 0.25)');
canvas0.renderAll();
var canvas1 = new fabric.Canvas('c1');
canvas1.setBackgroundColor('rgba(92, 18, 18, 0.25)');
canvas1.renderAll();
// add loaded image on left canvas
var onImageLoaded = function(oImg) {
oImg.originX = "center";
oImg.originY = "center";
oImg.left = this.x;
oImg.top = this.y;
canvas0.add(oImg);
oImg.canvas = canvas0;
imgArrow = oImg;
};
var config = { crossOrigin: 'anonymous' };
var baseUrl = "http://mbnsay.com/rayys/images";
var url0 = baseUrl + "/arrow-right-green.png";
var url1 = baseUrl + "/arrow-right-icon.png";
var url2 = baseUrl + "/arrow-right-blue.png";
// load some images
fabric.Image.fromURL(url0, onImageLoaded.bind({ x: 56, y: 96 }), config);
fabric.Image.fromURL(url0, onImageLoaded.bind({ x: 156, y: 96 }), config);
fabric.Image.fromURL(url1, onImageLoaded.bind({ x: 56, y: 2*96 }), config);
fabric.Image.fromURL(url1, onImageLoaded.bind({ x: 156, y: 2*96 }), config);
fabric.Image.fromURL(url2, onImageLoaded.bind({ x: 56, y: 3*96 }), config);
fabric.Image.fromURL(url2, onImageLoaded.bind({ x: 156, y: 3*96 }), config);
Step 2 – subscribe object:moving events on both canvases, and watch when object center crossing the canvas border. When object crosses the border, it has to be
remove from source canvas,
paste into destination canvas,
migrate internal canvas transformations (will be explained separately)
var onObjectMoving = function(p) {
var viewport = p.target.canvas.calcViewportBoundaries();
if (p.target.canvas === canvas0) {
if (p.target.left > viewport.br.x) {
console.log("Migrate: left -> center");
migrateItem(canvas0, canvas1, p.target);
return;
}
}
if (p.target.canvas === canvas1) {
if (p.target.left < viewport.tl.x) {
console.log("Migrate: center -> left");
migrateItem(canvas1, canvas0, p.target);
return;
}
}
};
canvas0.on("object:moving", onObjectMoving);
canvas1.on("object:moving", onObjectMoving);
Step 3 – The core of the solution, migrate object between canvases not interrupting mouse manipulations. Hard to explain, just follow the comments in code.
var migrateItem = function(fromCanvas, toCanvas, pendingImage) {
// Just drop image from old canvas
fromCanvas.remove(pendingImage);
// We're going to trick fabric.js,
// so we keep internal transforms of the source canvas,
// in order to inject it into destination canvas.
var pendingTransform = fromCanvas._currentTransform;
fromCanvas._currentTransform = null;
// Make shortcuts for fabric.util.removeListener and fabric.util.addListener
var removeListener = fabric.util.removeListener;
var addListener = fabric.util.addListener;
// Re-arrange subscriptions for source canvas
{
removeListener(fabric.document, 'mouseup', fromCanvas._onMouseUp);
removeListener(fabric.document, 'touchend', fromCanvas._onMouseUp);
removeListener(fabric.document, 'mousemove', fromCanvas._onMouseMove);
removeListener(fabric.document, 'touchmove', fromCanvas._onMouseMove);
addListener(fromCanvas.upperCanvasEl, 'mousemove', fromCanvas._onMouseMove);
addListener(fromCanvas.upperCanvasEl, 'touchmove', fromCanvas._onMouseMove, {
passive: false
});
if (isTouchDevice) {
// Wait 500ms before rebinding mousedown to prevent double triggers
// from touch devices
var _this = fromCanvas;
setTimeout(function() {
addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
}, 500);
}
}
// Re-arrange subscriptions for destination canvas
{
addListener(fabric.document, 'touchend', toCanvas._onMouseUp, {
passive: false
});
addListener(fabric.document, 'touchmove', toCanvas._onMouseMove, {
passive: false
});
removeListener(toCanvas.upperCanvasEl, 'mousemove', toCanvas._onMouseMove);
removeListener(toCanvas.upperCanvasEl, 'touchmove', toCanvas._onMouseMove);
if (isTouchDevice) {
// Unbind mousedown to prevent double triggers from touch devices
removeListener(toCanvas.upperCanvasEl, 'mousedown', toCanvas._onMouseDown);
} else {
addListener(fabric.document, 'mouseup', toCanvas._onMouseUp);
addListener(fabric.document, 'mousemove', toCanvas._onMouseMove);
}
}
// We need this timer, because we want Fabric.js to complete pending render
// before we inject, because it causes some unpleasant image jumping.
setTimeout(function() {
// Add image to destination canvas,
pendingImage.scaleX *= -1;
pendingImage.canvas = toCanvas;
pendingImage.migrated = true;
toCanvas.add(pendingImage);
// and inject transforms from source canvas
toCanvas._currentTransform = pendingTransform;
// as we have mirrored the image, we mirror transforms too
toCanvas._currentTransform.scaleX *= -1;
toCanvas._currentTransform.original.scaleX *= -1;
// finally don't forget to make pasted object selected
toCanvas.setActiveObject(pendingImage);
}, 10);
};
Have fun!
Use
canvas.observe("object:moving", function (event) {});
If event.e.clientY and event.e.clientX are outside the canvas, then:
var activeObject = canvas.getActiveObject();
Store in global dragImage var
activeObject.clone(function (c) { dragImage = c; });
canvas.remove(activeObject);
Then in window mouse move event you can place an img with src = dragImage.src and follow the cursor.
function mousemove(e){
if (dragImage != null) {
$("#dragimage").show();
$("#dragimage").css("left", e.clientX);
$("#dragimage").css("top", e.clientY);
return;
}else{
$("#dragimage").hide();
}
}
On a window event mouseup, if dragImage != null and new coordinates are inside a fabric.js canvas, just newcanvas.add(dragImage).
mouseup event:
if (dragImage != null) {
$([canvas, canvas2]).each(function (i, v) {
if (Intersect([event.clientX, event.clientY],$(v.wrapperEl))) {
dragImage.left = event.clientX - $(v.wrapperEl).offset().left;
dragImage.top = event.clientY - $(v.wrapperEl).offset().top;
v.add(dragImage);
}
});
dragImage = null;
}
Helper Intersect function:
function Intersect(point, element) {
return ( point[0] > element.offset().left
&& point[0] < element.offset().left + element.width()
&& point[1] < element.offset().top + element.height()
&& point[1] > element.offset().top
);
}
css for #dragimage:
#dragimage
{
opacity:0.5;
max-width:100px;
max-height:200px;
position:fixed;
top:0px;
left:0px;
z-index:90000;
}
I can't do a fiddle but i implemented this on our mega huge photo album editor in less than 30 minutes. Works for text too but for text you must use dragImage=getActiveObject().clone()
Any questions feel free to ask.

How to make a graphics element draggable with google-closure?

How to make google closure graphics elements draggable and respond to events otherwise?
Here's what I have so far. I have the circle, but can't drag it yet :).
Thanks.
goog.require('goog.dom');
goog.require('goog.graphics');
goog.require('goog.events');
goog.require('goog.fx.Dragger');
goog.provide('graphics_test');
graphics_test = function(){
var canvas = goog.dom.createDom('div', {'id':'canvas'});
goog.dom.appendChild(document.body, canvas);
var g = goog.graphics.createGraphics(600,400);
var fill = new goog.graphics.SolidFill('yellow');
var stroke = new goog.graphics.Stroke(1,'black');
circle = g.drawCircle(300, 200, 50, stroke, fill);
var dragger = new goog.fx.Dragger(circle,circle);
g.render(goog.dom.$('canvas'));
};
I had to use a dragger in my own project and couldn't get goog.fx.Dragger to work. However, I implemented my own draggable. Its actually much smaller and pretty simple. Here is a gist:
var graphic = new goog.graphics.ext.Graphics(1000, 500);
var group = new goog.graphics.ext.Group(graphic);
group.setLeft(20, true);
group.setTop(20, true);
group.setWidth(600, true);
group.setHeight(200);
var fill = new goog.graphics.SolidFill('yellow');
var stroke = new goog.graphics.Stroke(2, 'green');
var bg = new goog.graphics.SolidFill('#eeeeee');
var outline = new goog.graphics.Stroke(1, '#333333');
var path = new goog.graphics.ext.Path().moveTo(0, 0).lineTo(20, 0).
lineTo(10, 20).close();
var shape = new goog.graphics.ext.Shape(group, path);
shape.setLeft(10, true);
shape.setTop(10, true);
shape.setWidth('10%', true);
shape.setHeight('10%');
shape.setStroke(stroke);
shape.setFill(fill);
var ellipse = new goog.graphics.ext.Ellipse(group);
ellipse.setCenter(0, true);
ellipse.setMiddle(0, true);
ellipse.setWidth(120, true);
ellipse.setHeight(60);
ellipse.setStroke(stroke);
ellipse.setFill(fill);
goog.events.listen(group.getWrapper(), 'mousedown', function(e) {
group.startOffsetX = e.offsetX;
group.startOffsetY = e.offsetY;
group.dragging = true;
});
goog.events.listen(group.getWrapper(), 'mouseup', function(e) {
group.dragging = false;
});
goog.events.listen(group.getWrapper(), 'mousemove', function(e) {
if(group.dragging) {
group.setPosition(group.getLeft() + (e.offsetX-group.startOffsetX),
group.getTop() + (e.offsetY-group.startOffsetY));
group.startOffsetX = e.offsetX;
group.startOffsetY = e.offsetY;
};
});
goog.events.listen(group.getWrapper(), 'mouseout', function(e) {
group.dragging = false;
});
graphic.render(document.body);
You can of course listen on any single shape too (rectangle/ellipse or even a path) by listening to it instead of the group itself. I feel this method gives you more flexibility (think restricting the movement of the entire group to the canvas or some custom bounds!)
I purposely omitted the fifth argument(opt_handler) from goog.events.listen so as to make this code more readable.
Hope this helps :)

Categories

Resources