I've got a project where I'm using EaselJS to create a fill (rectangle) and text over it inside a container. My objective is to make this rectangle and text draggable to move it over the canvas. This is already done and working well.
My problem is when I try to resize the rectangle using scaleX and scaleY using a onMouseOver handler. This is indeed done, BUT the rectangle just moves away from it's initial point to some other location.
I've read I need to use the regX and regY properties to override this, but in the context of my code I just can't. What am I doing wrong?
Here's my Javascript code:
(function(){
var stage, myCanvas;
var update = true;
this.init = function() {
myCanvas = document.getElementById("stageCanvas");
stage = new createjs.Stage(myCanvas);
stage.enableMouseOver();
stage.mouseEnabled = true;
stage.mouseMoveOutside = true;
// Reference Shape
var rectFijo0 = new createjs.Shape();
rectFijo0.graphics.beginFill("#DCD8D4").rect(140,160,78,35);
stage.addChild(rectFijo0);
// Shape
var rectOpt0 = new createjs.Shape();
rectOpt0.graphics.beginFill("#C51A76").rect(140,160,78,35);
txtOpt0 = new createjs.Text("TEST","bold 20px","#FFF");
txtOpt0.textAlign ="center";
txtOpt0.x = 50;
txtOpt0.y = 50;
// Container
var containerOpt0= new createjs.Container();
containerOpt0.mouseEnabled = true;
//#######
// Probably here is my problem. I don't understand why if I use regX and regY the rectangle moves the lower right corner to the center, instead of just using this point as a registration point. Why? What I am not understanding?
//containerOpt0.regX = 78/2;
//containerOpt0.regY = 35/2;
//#######
containerOpt0.onPress = pressHandler;
containerOpt0.onMouseOver = mouseOverHandler;
containerOpt0.addChild(rectOpt0, txtOpt0);
stage.addChild(containerOpt0);
stage.update();
createjs.Ticker.setFPS(60);
createjs.Ticker.addEventListener("tick", tick);
}
function pressHandler(e){
// onPress Handler to drag
var offset = {x:e.target.x-e.stageX, y:e.target.y-e.stageY};
e.onMouseMove = function(ev) {
e.target.x = ev.stageX+offset.x;
e.target.y = ev.stageY+offset.y;
update = true;
}
}
function mouseOverHandler(e){
e.target.scaleX = .5;
e.target.scaleY = .5;
update = true;
}
function tick() {
if (update) {
update = false;
stage.update();
}
}
window.onload = init();
}());
Here's my JS Fiddle example so you can see exactly what's going on. Just drag the mouse over the rectangle to see what I mean. It must be something easy to achieve but I'm not certain how.
You issue is, that you are drawing your rects not at 0|0, the "standard" way is to draw your shape starting at 0|0 and then position the shape itself somewhere through .x and .y
rectOpt0.graphics.beginFill("#C51A76").rect(140,160,78,35);
=> changed to
rectOpt0.graphics.beginFill("#C51A76").rect(0,0,78,35);
and additionally I then placed the container at 140|160 + the regX/Y offset:
containerOpt0.regX = 78/2; //regX/Y to have is scale to the center point
containerOpt0.regY = 35/2;
containerOpt0.x = 140 + 78/2; //placement + regX offset/correction
containerOpt0.y = 160 + 35/2;
here's the updated fiddle: http://jsfiddle.net/WfMGr/2/
Related
I have made an edit form but this edit form also includes a drawing on a canvas. Everything is working okay except for the eraser I made for the canvas. The eraser works perfectly if you're making a drawing on a blank canvas but since this is for an edit page which means it already has a drawing on it. My main issue right now is that whenever I use the eraser, the previous drawing gets removed from the canvas leaving only the new marks you made. Can anyone help me with this one?
var stage, wrapper, erase=0;
function init(){
var stage = new createjs.Stage("canvas");
createjs.Ticker.addEventListener("tick", stage);
createjs.Touch.enable(stage);
stage.addChild(new createjs.Text("", "40px Arial", "#000000").set({x:200,y:200}));
// Set up the container. We use it to draw in, and also to get mouse events.
var wrapper = new createjs.Container();
wrapper.hitArea = new createjs.Shape(new createjs.Graphics().f("#000").dr(0,0,800,600));
wrapper.cache(0,0,800,600); // Cache it.
stage.addChild(wrapper);
// Create the shape to draw into
var drawing = new createjs.Shape();
wrapper.addChild(drawing);
var lastPoint = new createjs.Point();
// retrieves image url for previous drawing
var path = document.getElementById("hidden").value;
var prevDraw = new createjs.Bitmap(path);
prevDraw.image.onload = function(){
prevDraw.x = 0;
prevDraw.y = 0;
wrapper.addChild(prevDraw);
wrapper.updateCache();
stage.update();
}
wrapper.addEventListener("mousedown", function(event){
// Store the position. We have to do this because we clear the graphics later.
lastPoint.x = event.stageX;
lastPoint.y = event.stageY;
if(document.getElementById('toggle').checked){
erase = 1;
}else{
erase = 0;
}
wrapper.addEventListener("pressmove", function(event){
// Draw a round line from the last position to the current one.
drawing.graphics.ss(5, "round").s("#ff0000");
drawing.graphics.mt(lastPoint.x, lastPoint.y);
drawing.graphics.lt(event.stageX, event.stageY);
// Update the last position for next move.
lastPoint.x = event.stageX;
lastPoint.y = event.stageY;
// Draw onto the canvas, and then update the container cache.
wrapper.updateCache(erase==1?"destination-out":"source-over");
drawing.graphics.clear();
});
// Listen for mousemove
});
function clear(){
stage.removeAllChildren();
stage.update();
init2();
}
document.getElementById("clear").onclick = function(){
clear()
};
function reset(){
stage.removeAllChildren();
stage.update();
init();
}
document.getElementById("reset").onclick = function(){
reset()
};
}
I've created a hexagonal grid using this JS library. The grid does get properly painted onto the Canvas. My issue is with trying to introduce events on mouse events. For instance, when I hover over a certain hexagon I want it's background color to change.
At the moment, only the last created hexagon will change on mouseover regardless of which hexagon the cursor is hovering over. How can I make the event listener update the specific hexagon over which the cursor exists?
If this is not possible due to painted objects becoming "rasterized" into the canvas, what alternate approach would be recommended?
The code is below:
<canvas id="stage"></canvas>
<script>
var element = document.getElementById("stage");
element.height = window.innerHeight;
element.width = window.innerWidth;
var stage = new createjs.Stage("stage");
stage.x = window.innerWidth/2;
stage.y = window.innerHeight/2;
stage.enableMouseOver();
var grid = new Grid();
grid.tileSize = 55;
var stageTransformer = new StageTransformer().initialize({ element: element, stage: stage });
stageTransformer.addEventListeners();
var tick = function (event) { stage.update(); };
var colorHexagon = function(hexagon, fill) {
hexagon.graphics
.beginFill(fill)
.beginStroke("rgba(50,50,50,1)")
.drawPolyStar(0, 0, grid.tileSize, 6, 0, 0);
};
var coordinates = grid.hexagon(0, 0, 3, true)
for (var i = 0; i < coordinates.length; i++) {
var q = coordinates[i].q,
r = coordinates[i].r,
center = grid.getCenterXY(q, r),
hexagon = new createjs.Shape();
hexagon.cursor = "pointer";
hexagon.addEventListener("mouseover", function() {
colorHexagon(hexagon, "rgba(50,150,0,1)")
});
hexagon.addEventListener("mouseout", function() {
colorHexagon(hexagon, "rgba(150,150,150,1)")
});
hexagon.q = q;
hexagon.r = r;
hexagon.x = center.x;
hexagon.y = center.y;
colorHexagon(hexagon, "rgba(150,150,150,1)");
stage.addChild(hexagon);
stage.update();
}
tick();
createjs.Ticker.setFPS(30);
createjs.Ticker.addEventListener("tick", tick);
</script>
This should work.
stage.addEventListener("mouseover", function(evt) {
colorHexagon(evt.target, "rgba(50,150,0,1)")
});
stage.addEventListener("mouseout", function(evt) {
colorHexagon(evt.target, "rgba(150,150,150,1)")
});
you can put them at the very bottom of your script. They don't have to be in a loop.
Bind your event handlers to the container of the hexagons instead of to each hexagon seperately and find the hexagon hovered over by using the coordinates of the mouse. And check the docs of the library, maybe they have a method for that included.
Im new to using canvas but wonder if i could get some help or advice on where to start with this. i currently have a circle drawn on canvas and have tried multiple ways i thought might work but cant get my head round it. When searching online i can only really find help where the shapes are drawn in canvas itself.
Here is what i have so far: JSFiddle
JavaScript:
var one = document.getElementById("canvOne");
var canvOne = one.getContext("2d");
var img = new Image();
img.onload = function(){
canvOne.drawImage(img, 10, 10);
};
img.src = "img/circle.jpg";
function oneStart(){
for(var i = 0; i < 300; i++){
img.style.left = i + 'px';
setTimeout(oneStart, 1000);
}
};
can anyone give me a hand with this please?
jsFiddle : http://jsfiddle.net/8eLL7L5L/3/
javascript
//canvas one
var one = document.getElementById("canvOne");
var canvOne = one.getContext("2d");
var img = new Image();
var animate = false;
var circlePosX = 10;
var circlePosY = 10;
img.onload = function () {
canvOne.drawImage(img, circlePosX, circlePosY);
};
img.src = "http://www.alexvolley.co.uk/other/circle.jpg";
var start = function()
{
animate = true;
}
var stop = function()
{
animate = false;
}
setInterval(function () {
// Only update circle position if animate is true
if (animate) {
circlePosX += 1;
canvOne.fillStyle = "#FFF";
canvOne.fillRect(0, 0, one.width, one.height);
canvOne.drawImage(img, circlePosX, circlePosY);
}
}, 3);
All I have done is created 3 variables and added a few little functions, the circle's XPos and YPos are now stored so we can access them, I have also created a bool variable animate which is used to check if we should animate the circle.
The start function simply sets animate to true
The stop function simple sets animate to false
the setInterval just keeps running the same code over and over every 3 miliseconds, all this does is clears the canvas and then redraws the circle. If we don't clear the canvas you would see multiple circles appear when animate is true.
I hope this helped
I am developing a canvas paint but I want to have an eraser there. So I use this lines to erase de content but when I click is clear the whole canvas.
//undo tool
var undo = new createjs.Bitmap(app.loader.getResult('undo'));
undo.name = 'undo';
undo.x = brush.x + 90;
undo.y = brush.y;
undo.addEventListener('click', this.undoHandler);
this.toolsContainer.addChild(undo);
//trash tool
var clear = new createjs.Bitmap(app.loader.getResult('clear'));
clear.name = 'clear';
clear.x = undo.x + 90;
clear.y = undo.y;
clear.addEventListener('click', this.clearHandler);
this.toolsContainer.addChild(clear);
undoHandler:function(){
if(tools.undoArray.length){
var lastItem = tools.undoArray.pop();
app.container.removeChild(lastItem);
var lastItem2 = tools.undoArray2.pop();
app.container.removeChild(lastItem2);
var lastItem3 = tools.undoArray3.pop();
app.container.removeChild(lastItem3);
app.stage.update();
}
},
clearHandler:function(){
app.container.removeAllChildren();
app.container.updateCache(clearhandler?"destination-out":"source-over");;
app.stage.update();
},
I trying to develop something like this erase
http://jsfiddle.net/lannymcnie/ZNYPD/
any idea?
well here http://jsfiddle.net/lannymcnie/ZNYPD/, the key is this:
wrapper.updateCache(erase?"destination-out":"source-over");
so...
var stage, wrapper,erase;
function init() {
var stage = new createjs.Stage("canvas");
createjs.Ticker.addEventListener("tick", stage);
// Add some text to draw on top of (also instructions)
stage.addChild(new createjs.Text("Click and Drag to Draw", "40px Arial", "#000000").set({x:200,y:200}));
// Set up the container. We use it to draw in, and also to get mouse events.
var wrapper = new createjs.Container();
wrapper.hitArea = new createjs.Shape(new createjs.Graphics().f("#000").dr(0,0,800,600));
wrapper.cache(0,0,800,600); // Cache it.
stage.addChild(wrapper);
// Create the shape to draw into
var drawing = new createjs.Shape();
wrapper.addChild(drawing);
var lastPoint = new createjs.Point();
wrapper.addEventListener("mousedown", function(event) {
// Store the position. We have to do this because we clear the graphics later.
lastPoint.x = event.stageX;
lastPoint.y = event.stageY;
erase = Math.floor(Math.random()*2);
wrapper.addEventListener("pressmove", function(event){
// Draw a round line from the last position to the current one.
drawing.graphics.ss(20, "round").s("#ff0000");
drawing.graphics.mt(lastPoint.x, lastPoint.y);
drawing.graphics.lt(event.stageX, event.stageY);
// Update the last position for next move.
lastPoint.x = event.stageX;
lastPoint.y = event.stageY;
// Draw onto the canvas, and then update the container cache.
wrapper.updateCache(erase==1?"destination-out":"source-over");
drawing.graphics.clear();
});
// Listen for mousemove
});
}
$(function(){
init();
})
the only difference is that the drawing is based on a random from 0 to 1 because in my example the erase gets these values from here erase = Math.floor(Math.random()*2);
I have achieved this by maintaining a array of mid points and using the globalCompositeOperation as destination-out and source-over to make a transparent eraser trail .
Following is the code that you need to use with a mouse move function
`var handleMouseMove = function (event) {
midPt = new createjs.Point(oldPt.x + stage.mouseX>>1, oldPt.y+stage.mouseY>>1);
if(curTool.type=="eraser"){
var tempcanvas = document.getElementById('drawcanvas');
var tempctx=tempcanvas.getContext("2d");
tempctx.beginPath();
tempctx.globalCompositeOperation = "destination-out";
tempctx.arc(midPt.x, midPt.y, 20, 0, Math.PI * 2, false);
tempctx.fill();
tempctx.closePath();
tempctx.globalCompositeOperation = "source-over";
drawingCanvas.graphics.clear();
// keep updating the array for points
arrMidPtx.push(midPt.x);
arrMidPty.push(midPt.y);
stage.addChild(drawingCanvas);
stage.update();
}
else if ( curTool.type=="pen"){
drawingCanvas.graphics.clear().setStrokeStyle(stroke, 'round', 'round').beginStroke(color).moveTo(midPt.x, midPt.y).curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);
arrMidPtx.push(midPt.x);
arrMidPty.push(midPt.y);
arrOldPtx.push(oldPt.x);
arrOldPty.push(oldPt.y);
arrOldMidPtx.push(oldMidPt.x);
arrOldMidPty.push(oldMidPt.y);
oldPt.x = stage.mouseX;
oldPt.y = stage.mouseY;
oldMidPt.x = midPt.x;
oldMidPt.y = midPt.y;
stage.addChild(drawingCanvas);
stage.update();
}
};`
I need to have a couple of images on one canvas.
I have a trouble with a function clear(); which is used when I need to drag images on canvas.
The problem is that we are Canvas appears only on the last image.
I try to use context.save() and context.restore() but it was not usefull in my case.
switch(i)
{
case 0:
challengerImg = new Image();
challengerImg.onload = function(){
drawImage(this,x,y,i);
};
challengerImg.src = "<?php echo $base_url; ?>/themes/bartik/images/sheep.png";
break;
case 1:
tshirt = new Image();
tshirt.onload = function(){
drawImage(this,x,y,i);
};
tshirt.src = "<?php echo $base_url; ?>/themes/bartik/images/tshirt.png";
break;
}
And function which draw on canvas:
function drawImage(challengerImg,x,y,i){
console.log("Function drawImage start");
var events = new Events("layer0");
var canvas = events.getCanvas();
var context = events.getContext();
var rectX = x;
var rectY = y;
var draggingRect = false;
var draggingRectOffsetX = 0;
var draggingRectOffsetY = 0;
events.setStage(function(){
var mousePos = this.getMousePos();
if (draggingRect) {
rectX = mousePos.x - draggingRectOffsetX;
rectY = mousePos.y - draggingRectOffsetY;
}
this.clear(); //Here is trouble
this.beginRegion();
context.drawImage(challengerImg, rectX, rectY, challengerImg.width, challengerImg.height);
// draw rectangular region for image
context.beginPath();
context.rect(rectX, rectY, challengerImg.width, challengerImg.height);
context.closePath();
this.addRegionEventListener("mousedown", function(){
draggingRect = true;
var mousePos = events.getMousePos();
draggingRectOffsetX = mousePos.x - rectX;
draggingRectOffsetY = mousePos.y - rectY;
});
this.addRegionEventListener("mouseup", function(){
draggingRect = false;
});
this.addRegionEventListener("mouseover", function(){
document.body.style.cursor = "pointer";
});
this.addRegionEventListener("mouseout", function(){
document.body.style.cursor = "default";
});
this.closeRegion();
});
}
context.save and context.restore only works for state of the context (transformation, globalAlpha,...), but not for what is rendered inside.
When you clear your context, it makes it empty.
What you have to do is to :
catch mouse events and change position variables
clear the canvas
redraw all images at their new position
save(), restore() do not handle bitmap data at all :).
To drag stuff around, basically you need to have an offscreen canvas which doesn't contain the element you are drawing, and after each update draw the offscreen canvas plus the element being dragged.
It's probably simpler to use a library that already does it, most do, like http://www.html5canvastutorials.com/labs/html5-canvas-interactive-scatter-plot-with-20000-nodes-using-kineticjs/
The canvas is a flat object - once you draw an image onto it, the image has no more context as it renders as part of the canvas. You can either create an array of rectangle objects with their coordinates to keep track of what's already on the canvas or test the mouse position for the background color to determine whether there is an image or not. Then you can remove the item where the event happened by clearing the canvas and redrawing all the other items again.
Hope this helps!