I have a project on svg-edit where i have to create polygon on mouse-click , the svg-canvas (not HTML5 canvas suppose as drawing board) is zoomable, i can create polygon on 100% zoom but when i zoomin or zoomout i can't, actually i am unable to get right x and y position after zoom.as you can see in image.
I had tried this method to get points--
//Container 1440*1920
var svgcanvas = document.getElementById("svgcanvas");
//zoom
var initialZoom = 1440 * 1920;
zoomWidth = parseFloat(svgcanvas.style.width);
zoomHeight = parseFloat(svgcanvas.style.height);
currentZoom = zoomHeight * zoomWidth;
zoom = currentZoom / initialZoom;
//points
var rect = event.target.getBoundingClientRect();
var x1 = ((event.clientX) / zoom) - rect.left;
var y1 = ((eevent.clientY) / zoom) - rect.top;
and my task is
It seems you are not interacting with the svg-edit API at all. Things get much easier if you do.
// do not interact with the DOM element, but with the API object
var svgcanvas = svgEditor.canvas;
// your event listener function
function listener (event) {
var rect = svgcanvas.getBBox(event.target);
var x1 = rect.x;
var y = rect.y;
...
}
No need to handle zooming; the SvgCanvas instance does that for you.
Related
The function below takes Layer X and makes Layer X copy.
It takes Layer X copy and while maintaining the ratio adjusts the height to 600px. var newdLayer
It takes Layer X and while maintaining the ratio adjusts the width to 900px and applies the Gaussian Blur. var blur.
It then merges Layer X and Layer X copy.
The problem I am having is that if, for example, I execute the script on the 4th layer in the Layers Panel after the script is executed the Layer becomes 1st layer in the Layers Panel.
Therefore, it takes the layer out of sequence.
How to make sure that the layer retains its position in the sequence of layers in the Layers Panel?
(function()
{
var startRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var docRef = activeDocument;
var blur = docRef.activeLayer;
// since we resize based on the initial size of the source layer,
// we don't need to get the bounds twice
var bounds = blur.bounds;
var height = bounds[3].value - bounds[1].value;
var width = bounds[2].value - bounds[0].value;
/////////////////////////////////////////////////////////////////////////////////////
// Centering the layer
// Getting center coordinates of the document
var docCenterW = docRef.width.as("px") / 2;
var docCenterH = docRef.height.as("px") / 2;
// getting values to translate the layer.
var deltaX = Math.round(docCenterW - (bounds[0].value + width / 2));
var deltaY = Math.round(docCenterH - (bounds[1].value + height / 2));
blur.translate(deltaX, deltaY);
/////////////////////////////////////////////////////////////////////////////////////
var newdLayer = blur.duplicate();
// declare 2 different vars for your sizes (there are better ways to do this, but
// since you say you aren't a JavaScript pro, I figured I'd keep it simple)
var newSize600 = (100 / height) * 600;
var newSize900 = (100 / width) * 900;
// resize your layers
newdLayer.resize(newSize600, newSize600, AnchorPosition.MIDDLECENTER);
blur.resize(newSize900, newSize900, AnchorPosition.MIDDLECENTER);
// apply blur
blur.applyGaussianBlur(5);
// below creates the group, moves the layers to it and merges them. Feel free to just include this part
// at the end of your function if you don't want to use the modified code above.
// create a new layer set
var groupOne = docRef.layerSets.add();
// move the blur layer inside the layer set and name the layer for posterity
blur.move(groupOne, ElementPlacement.INSIDE);
blur.name = "blur";
// move the newdLayer inside and rename
newdLayer.move(groupOne, ElementPlacement.INSIDE);
newdLayer.name = "newdLayer";
// merge the layer set and name the new layer
var mergedGroup = groupOne.merge();
mergedGroup.name = "newdLayer + blur";
app.preferences.rulerUnits = startRulerUnits;
})();
Yes, when created using DOM a new group goes to the top of the stack. Creating a group using AM code doesn't have this particularity, so you can use that instead.
(function()
{
var startRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var docRef = activeDocument;
var blur = docRef.activeLayer;
// since we resize based on the initial size of the source layer,
// we don't need to get the bounds twice
var bounds = blur.bounds;
var height = bounds[3].value - bounds[1].value;
var width = bounds[2].value - bounds[0].value;
/////////////////////////////////////////////////////////////////////////////////////
// Centering the layer
// Getting center coordinates of the document
var docCenterW = docRef.width.as("px") / 2;
var docCenterH = docRef.height.as("px") / 2;
// getting values to translate the layer.
var deltaX = Math.round(docCenterW - (bounds[0].value + width / 2));
var deltaY = Math.round(docCenterH - (bounds[1].value + height / 2));
blur.translate(deltaX, deltaY);
/////////////////////////////////////////////////////////////////////////////////////
var newdLayer = blur.duplicate();
// declare 2 different vars for your sizes (there are better ways to do this, but
// since you say you aren't a JavaScript pro, I figured I'd keep it simple)
var newSize600 = (100 / height) * 600;
var newSize900 = (100 / width) * 900;
// resize your layers
newdLayer.resize(newSize600, newSize600, AnchorPosition.MIDDLECENTER);
blur.resize(newSize900, newSize900, AnchorPosition.MIDDLECENTER);
// apply blur
blur.applyGaussianBlur(5);
// below creates the group, moves the layers to it and merges them. Feel free to just include this part
// at the end of your function if you don't want to use the modified code above.
// create a new layer set
createGroup();
var groupOne = docRef.activeLayer;
// move the blur layer inside the layer set and name the layer for posterity
blur.move(groupOne, ElementPlacement.INSIDE);
blur.name = "blur";
// move the newdLayer inside and rename
newdLayer.move(groupOne, ElementPlacement.INSIDE);
newdLayer.name = "newdLayer";
// merge the layer set and name the new layer
var mergedGroup = groupOne.merge();
mergedGroup.name = "newdLayer + blur";
app.preferences.rulerUnits = startRulerUnits;
function createGroup()
{
var desc22 = new ActionDescriptor();
var ref2 = new ActionReference();
ref2.putClass(stringIDToTypeID('layerSection'));
desc22.putReference(charIDToTypeID('null'), ref2);
desc22.putString(charIDToTypeID('Nm '), "Group 1");
executeAction(charIDToTypeID('Mk '), desc22, DialogModes.NO);
} // end of createGroup()
})();
What I'm trying to create is a small canvas widget that would allow a user to dynamically create a shape onto an image and then place it above an area that caught their interest, effectively it is a highlighter.
The problem is with adding a zoom function, as when I zoom onto the image I would like to ensure that;
There is no possible way for the dynamically created shape to be
dragged anywhere outside the image area. (completed - ish, relies on 2nd step)
You cannot drag the image out of the page view, the canvas area cannot show white space. Part of the image must always be shown, and fill the entire canvas area. (problem)
Here are two examples that I've drawn up, neither of which work correctly;
First example - getBoundingRect does not update and is bound to the image
Second example - getBoundingRect does update and is bound to the grouped object
From the link description you can see that I think I've narrowed the problem down, or at least noticed a key difference between the scripts with how the getBoundingRect behaves.
The first plunk seems to work fine, until you try to zoom in multiple times and at a greater zoom level, then it seems to start bugging out (may take a few clicks and a bit of messing around, it is very inconsistent). The second plunk is very jittery and doesn't work very well.
I've been stuck on this for a week or so now, and I'm at breaking point! So really hoping someone can point out what I'm doing wrong?
Code snippet below for first plunk;
// creates group
var objs = canvas.getObjects();
var group = new fabric.Group(objs, {
status: 'moving'
});
// sets grouped object position
var originalX = active.left,
originalY = active.top,
mouseX = evt.e.pageX,
mouseY = evt.e.pageY;
active.on('moving', function(evt) {
group.left += evt.e.pageX - mouseX;
group.top += evt.e.pageY - mouseY;
active.left = originalX;
active.top = originalY;
originalX = active.left;
originalY = active.top;
mouseX = evt.e.pageX;
mouseY = evt.e.pageY;
// sets boundary area for image when zoomed
// THIS IS THE PART THAT DOESN'T WORK
active.setCoords();
// SET BOUNDING RECT TO 'active'
var boundingRect = active.getBoundingRect();
var zoom = canvas.getZoom();
var viewportMatrix = canvas.viewportTransform;
// scales bounding rect when zoomed
boundingRect.top = (boundingRect.top - viewportMatrix[5]) / zoom;
boundingRect.left = (boundingRect.left - viewportMatrix[4]) / zoom;
boundingRect.width /= zoom;
boundingRect.height /= zoom;
var canvasHeight = canvas.height / zoom,
canvasWidth = canvas.width / zoom,
rTop = boundingRect.top + boundingRect.height,
rLeft = boundingRect.left + boundingRect.width;
// checks top left
if (rTop < canvasHeight || rLeft < canvasWidth) {
group.top = Math.max(group.top, canvasHeight - boundingRect.height);
group.left = Math.max(group.left, canvasWidth - boundingRect.width);
}
// checks bottom right
if (rTop > 0 || rLeft > 0) {
group.top = Math.min(group.top, canvas.height - boundingRect.height + active.top - boundingRect.top);
group.left = Math.min(group.left, canvas.width - boundingRect.width + active.left - boundingRect.left);
}
});
// deactivates all objects on mouseup
active.on('mouseup', function() {
active.off('moving');
canvas.deactivateAll().renderAll();
})
// sets group
canvas.setActiveGroup(group.setCoords()).renderAll();
}
EDIT:
I've added comments and tried to simplify the code in the plunks.
The relevant code starts within the if (active.id == "img") { code block.
I've put irrelevant code as functions at the bottom, they can largely be ignored. ( createNewRect() + preventRectFromLeaving() )
I've removed one of the plunks to avoid confusion.
Let me know if it helps, or If I should try to simplify further.
Thanks!
I think that the grouping was messing with the position of the background image. So, I tried removing the group when the image is moving and manually updating the position of the rect instead.
It sets the last position of the image before moving
var lastLeft = active.left,
lastTop = active.top;
And then it updates those and the position of the rect every time the image moves
rect.left += active.left - lastLeft;
rect.top += active.top - lastTop;
// I think this is needed so the rectangle can be re-selected
rect.setCoords();
lastLeft = active.left;
lastTop = active.top;
Since the image has to stay within the canvas, the rect stays inside the canvas, too, whenever the image moves. The rest of the code you wrote seemed to work fine.
http://plnkr.co/edit/6GGcUxGC7CjcyQzExMoK?p=preview
I have a list of photos in my webpage, I need to fire a jquery event when a photo is on a particular position (view port)
How do I achieve this?
var rect = document.getElementbyID('myId').getBoundingClientRect();
// You can access the rect attributes by using the code below, this gives you cordinates as per the view port.
var t = rect.top
var l = rect.left
var width = rect.width;
var height = rect.height;
// if you need the coordinates as per the absolute screen then you can do somehting like this
var left = l + window.screenX;
var top = t + window.screenY + (window.outerHeight - window.innerHeight);
var right = left + width;
var bottom = top + height;
I'm struggling with this for a while now. I'm drawing a grid on the canvas. I add a mousemove eventHandler and track the mouseX and mouseY positions. I would like to be able to calculate the distance from the mouse position to the item in the grid. I can't seem to get this right, I've tried a few different solutions, like adding a loop in the mousemove handler and using requestAnimationFrame, but both solutions are very slow.
Here's my code below:
function setupCanvas(){
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
blockWidth = width/2 - 150;
blockHeight = height/2 - 100;
gridArray = [];
gridWidthArray = [];
ctx.fillRect(0,0,width,height);
//drawGrid();
drawInitGrid();
canvas.addEventListener('mousemove',onMouseMoveHandler);
}
function drawInitGrid(){
for(x = 0; x<16; x++){
for(y = 0; y<11; y++){
var gridBlock = new GridBlock((blockWidth) + x*20, (blockHeight) + y*20, blockWidth, blockHeight);
gridBlock.render(ctx);
gridArray.push(gridBlock);
//gridWidthArray.push(gridBlock.xPos)
}
}
}
function onMouseMoveHandler(e){
if(containerBounds(e)){
mouseX = e.offsetX;
mouseY = e.offsetY;
console.log(mouseX, mouseY);
//console.log(gridWidthArray);
for(var grid in gridArray){
//console.log(gridArray[grid].xPos)
}
}
}
I've also tried adding a mouseevent in the GridBlock object, but that also doesn't seem to work.
You can calculate the distance between any 2 points like this:
var dx=point2.x-point1.x;
var dy=point2.y-point1.y;
var distance=Math.sqrt(dx*dx+dy*dy);
Also in your fiddle your mouse position calculation should account for the offset position of the canvas within the window:
var BB=canvas.getBoundingClientRect();
var offsetX=BB.left;
var offsetY=BB.top;
function onMouseMoveHandler(e){
var mouseX=parseInt(e.clientX-offsetX);
var mouseY=parseInt(e.clientY-offsetY);
}
[ Finding nearest point in grid ]
Assume you have a mouse position [mx,my] and assume you have a grid with its top-left at [0,0] and with its cell size at cellWidth X cellHeight.
Then you can calculate the grid cell closest to the mouse like this:
var cellX=parseInt((mx+cellWidth/2)/cellWidth)*cellWidth;
var cellY=parseInt((my+cellHeight/2)/cellHeight)*cellHeight;
Of course, if the grid's top-left is not at [0,0], you will have to adjust for the gridss offset.
I am creating a multiple choice quiz on a canvas.
Naturally for the options I used squares as checkBoxes. Now I have put an event Listener on clicking a checkbox, it should get colored. However the listener doesnt work on entire square only the border. May I know how can the problem be resolved and also I wanted to know how to know if a square is filled with a color.
Function for loading options with oIndex from 0,1, till numberOfOptions
pHeight is a a global variable, optionChecks- the checkbox and optionsClick- the invisible rectangle for clicking on
function loadOption(oIndex){
optionChecks[oIndex] = new createjs.Shape();
optionChecks[oIndex].graphics.beginStroke("blue");
optionChecks[oIndex].graphics.setStrokeStyle(2);
optionChecks[oIndex].graphics.drawRect( canvas2.width*0.05 ,pHeight+20 ,20 ,20);
stage.addChild(optionChecks[oIndex] );
stage.update();
var y1 = pHeight+15;
var ht = y1;
var maxWidth = canvas2.width *0.8;
var text = problemOptions[oIndex];
var lineHeight = 25;
var x = (canvas2.width - maxWidth) / 2;
var y = y1;//pHeight+15;
//function for wrapping text
wrapText(ctx2, text, x, y, maxWidth, lineHeight);
ht = wHeight +10;
stage.update();
optionClicks[oIndex] = new createjs.Shape();
optionClicks[oIndex].graphics.beginStroke("black");
optionClicks[oIndex].graphics.setStrokeStyle(2);
optionClicks[oIndex].graphics.drawRect( canvas2.width*0.05,y1,maxWidth-500 ,pHeight-y1+10);
optionClicks[oIndex].addEventListener("click", handleOption);
stage.addChild(optionClicks[oIndex] );
}
Thanks in advance.
You could draw another opaque rect and use the button's hitArea - http://createjs.com/Docs/EaselJS/classes/DisplayObject.html#property_hitArea