Redraw images on canvas - javascript

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!

Related

HTML5 Canvas - lines drawn disappears in a flash

I have two canvases layered one over the other.
<div class="center">
<canvas id="imgcanvas" style="position:absolute;margin:0 0 0 0;z-index:1"></canvas>
<canvas id="drawcanvas" style="position:absolute;margin:0 0 0 0;z-index:2"></canvas>
</div>
Below the canvas, I have a horizontal thumbnail pallete. Upon clicking the thumbnail, the clicked image renders in the image canvas. I am using the drawcanvas to draw lines on the canvas. The coordinates for drawing the lines are stored in the localStorage.
Upon clicking the thumbnail, along with loading the image, I am trying to draw the lines taking the coordinates from the localStorage. However, the lines get drawn and disappears in a flash of a second.
The following is the code:
//clicked_id is returned by the thumbnail
function clickedImage(clicked_id) {
localStorage.setItem('sel_id', clicked_id);
//Get the complete path of the image
var clickedImg = document.getElementById(clicked_id).src;
var activeImg = document.getElementById(clicked_id).src;
//store the complete path
localStorage.setItem("activeImg",activeImg);
var clickedImg = basename(clickedImg,"/");
localStorage.setItem("clickedImage", clickedImg);
var clickedImg = clickedImg.replace(/^.*[\\\/]/, '');
//draw lines on canvas
var canvas = document.getElementById("imgcanvas");
var regcanvas = document.getElementById("drawcanvas");
var ctx = canvas.getContext("2d");
var ctxr = regcanvas.getContext("2d");
var arr = new Array();
//Perform calculations to get the fourth coordinate
arr = fourthCoord(getArray(arr));
for (var i = 0; i < arr.length; i++) {
if (arr[i][0] === clickedImg) {
first.x = arr[i][3];
first.y = arr[i][4];
second.x = arr[i][5];
second.y = arr[i][6];
third.x = arr[i][7];
third.y = arr[i][8];
fourth.x = arr[i][9];
fourth.y = arr[i][10];
ctxr.lineCap = "round";
ctxr.lineWidth = 3;
ctxr.strokeStyle = '#f4d03f';
ctxr.beginPath();
ctxr.moveTo(first.x,first.y);
ctxr.lineTo(second.x,second.y);
ctxr.lineTo(third.x,third.y);
ctxr.lineTo(fourth.x,fourth.y);
ctxr.lineTo(first.x,first.y);
ctxr.stroke();
console.log(first,second,third,fourth);
}
}
//Load the image
var thumbNails = document.getElementById("loaded_img_panel");
var pic = new Image();
pic.onload = function() {
canvas.width = pic.width;
canvas.height = pic.height;
regcanvas.width = pic.width;
regcanvas.height = pic.height;
ctx.drawImage(pic, 0,0)
}
thumbNails.addEventListener('click',function(event) {
pic.src = event.target.src;
});
}
The getArray() function gets the coordinates for the lines. I am using the same codes on a sample canvas, where clicking on the canvas (instead of clicking on the image) draws the lines. However on this application, the lines get drawn (which I could see for a fraction of a second) and then disappears.
Trial 1:
I called in two functions onclick of thumbNail - clickedImage() and drawAnnotations(). I moved all the codes pertaining to drawing lines into drawAnnotations(). This did not work.
Trial 2:
I called in the first function clickedImage() on mousedown event of the thumbnail and drawAnnotations() on the mouseup event of the thumbnail. This too did not work.
Trial 3:
I created a window.addEventListener...and called drawAnnotation function onload
window.addEventListener("DOMContentLoaded", function(event) {
document.getElementById('drawcanvas').addEventListener("load", drawAnnotations);
});
this too did not work...meaning...it draws the lines...but disappears in a flash.

How to make a canvas eraser for an Edit page?

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()
};
}

Update canvas on mouse events

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.

How to create an eraser in EaselJS?

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();
}
};`

Resize a draggable fill with EaselJS

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/

Categories

Resources