Swapping Cropped Images in Canvas - javascript

I want to swap two circular cuts from an image and swap their locations and draw them to the canvas. I am having issues, however, drawing the second circular image. Interestingly in second if you uncomment the first line it draws that image, otherwise if you throw it at the end of the function, it doesn't draw (at least on top). If I comment out the first function, I get the image to draw on the canvas. The first function always executes before the second.
function first() {
ctx.drawImage(imgBig, 0, 0);
ctx.beginPath();
ctx.arc(imgObj1.x + imgObj1.width / 2, imgObj1.y + imgObj1.width / 2, imgObj1.width / 2, 0, 6.28, false);
ctx.clip();
ctx.stroke(); // the outline of circle
ctx.closePath();
ctx.drawImage(imgBig, imgObj2.x, imgObj2.y, imgObj2.width, imgObj2.height, imgObj1.x, imgObj1.y, imgObj1.width, imgObj1.height);
function second() {
// ctx.drawImage(imgCuttingBoard, 0, 0); // this will draw over canvas
ctx.beginPath();
ctx.arc(imgObj2.x + imgObj2.width / 2, imgObj2.y + imgObj2.width / 2, imgObj2.width / 2, 0, 6.28, false);
ctx.clip();
ctx.closePath();
ctx.drawImage(imgCuttingBoard, imgObj1.x, imgObj1.y, imgObj1.width, imgObj1.height, imgObj2.x, imgObj2.y,
imgObj2.width, imgObj2.height); // this doesn't draw on top of the image (might be drawing underneath?)
// ctx.drawImage(imgCuttingBoard, 0, 0); // this will not draw over canvas here
}

Assuming you have given the image time to fully load before trying to drawImage it. You did use image.onload and wait for image to load?
Then your problem is likely clipping ...
context.clip is cumulative. If one clip (clip#1) is applied to the canvas followed by another clip (clip#2), then the resulting clipping area is the intersection of clip#1 and clip#2. The resulting clip is not clip#2.
So if you want to undo clip#1 so you can use the full clip#2, you must wrap the first clip in context.save and context.restore.
Here's a slightly different way of doing it using a temporary canvas
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/faces%20no%20background.png";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height;
ctx.drawImage(img,0,0);
// do the swap
clipCircle(img,63.5,56,54,203,177);
clipCircle(img,203,177,54,63.5,56);
}
function clipCircle(img,sourceCX,sourceCY,r,newCX,newCY){
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
c.width=2*r;
c.height=2*r;
// define an clipping circle
cctx.beginPath();
cctx.arc(r,r,r,0,Math.PI*2);
// draw the source into the temp canvas
cctx.drawImage(img,-sourceCX+r,-sourceCY+r);
// draw the temp canvas onto the main canvas
ctx.drawImage(c,newCX-r,newCY-r);
}
body{ background-color: ivory; }
canvas{border:1px solid red; margin:0 auto; }
<h4>Swapped clipping on canvas<br>(top-left swapped with bottom-center)</h4>
<canvas id="canvas" width=300 height=300></canvas>
<h4>Original Image</h4>
<img src='https://dl.dropboxusercontent.com/u/139992952/multple/faces%20no%20background.png'>

Related

Rotate each context via JavaScript

image = new Image();
image.src = 'assets/img/image.png';
for (var i = 0; i < this.bombs.length; i++) {
var bomb = this.bombs[i];
ctx.drawImage(image, bomb.x - 2, bomb.y - 2, 15, 8);
}
This is an image in my canvas game and falling down from top. But I want, get images rotate random each state.
I tried this:
function drawRotated(degrees){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(canvas.width/2,canvas.height/2);
ctx.rotate(degrees*Math.PI/180);
ctx.drawImage(image,-image.width/2,-image.width/2);
ctx.restore();
}
Added this function in my image but its not working. How can I do it?
You're on the right track!
To rotate one (or more) images:
Translate to the center point where you want the image centered.
Rotate by the desired angle
Draw the image
Undo the transformations (undo translate & rotate).
A couple of changes:
You always translate to the middle of the canvas. If you want the bomb to drop you must increasingly translate down the canvas.
A typo: In drawImage you have used width for both the width & height argument.
An efficiency: context.save and context.restore are more expensive operations because they save & restore all of the canvas styles. Instead, it's more efficient to reset only your transformations (translate,rotate) with context.setTransform(1,0,0,1,0,0)
Here's your code refactored for these changes:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var bomb;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/danger.png";
function start(){
var aa=img.width*img.width;
var bb=img.height*img.height;
var cc=Math.sqrt(aa+bb);
bomb={
x:150,y:-img.height/2,
degrees:0,image:img,
maxRotatedHalfHeight:cc/2
};
requestAnimationFrame(animate);
}
function drawRotated(b){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(b.x,b.y);
ctx.rotate(b.degrees*Math.PI/180);
ctx.drawImage(b.image,-b.image.width/2,-b.image.height/2);
ctx.restore();
}
function animate(time){
drawRotated(bomb);
bomb.y+=1;
bomb.degrees+=1;
if(bomb.y<=canvas.height+bomb.maxRotatedHalfHeight){
requestAnimationFrame(animate);
}else{
alert('Animation is complete.');
}
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=175></canvas>

trying to colorize a simple PNG and draw inside a mask element in canvas

I tried to create an HTML canvas, place a rectangle... then INSIDE THAT rectangle, draw various shapes and an RGBa PNG... all of them clipped inside the dimensions of the rectangle. Then I tried to change the color of the PNG when you press an HTML button input. (Further comments in code.)
Heres the problems... A. You have to draw to a temporary canvas and apply "GlobalCompositeOperation source-atop" just AFTER the clipping rectangle. Everything drawn after that is successfully clipped into the rect shape. Then the whole thing is copied (drawn) to a MAIN canvas. I was told to do it this way in order for the programming to recognize MULTIPLE elements after a "composite" operation. I have to say this works BEAUTIFULLY!! but here's problem B...
To to a "getData" on an image (to change color), I think you have to place the image on a canvas, and doing all the image pixel manipulation screws up the "composite" operation, so I tried to draw the PNG to a THIRD canvas, do the pixel changes, and then draw it to the temporary canvas... adding it to the rest of the elements....THEEENNN draw it all to the main canvas. Does not work. See code. Please help, Im mad enough to chew neutronium.
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<canvas id="theCanvas" width="200" height="200" style="border:2px solid #000000;"></canvas>
<canvas id="tempCanvas" width="200" height="200" style="display:none;"></canvas>
<canvas id="anotherCanvas" width="200" height="200" style="display:none;"></canvas>
<form>
<input type="button" id="changeColor" value="Click to Change Color of Graphic">
</form>
<script type="text/javascript" src="hereWeGoAgain_GHELP.js"></script>
</body>
</html>
//------------------------------------- JS
window.addEventListener("load", eventWindowLoaded, false);
function eventWindowLoaded () {
canvasApp();
}
function canvasApp() {
var canvas = document.getElementById('theCanvas');// the main canvas, where it all goes in the end
var context = canvas.getContext('2d');
var tempCanvas = document.getElementById('tempCanvas');// the canvas to do my "source-atop" stuff...
var tempContext = tempCanvas.getContext('2d');
var anotherCanvas = document.getElementById('anotherCanvas');
var anotherContext = anotherCanvas.getContext('2d');
// ...and Im thinking I should draw the RGBA PNG here, before placing it in the temp canvas, with the other elements
var cc = document.getElementById('changeColor');
cc.addEventListener('click', function(){changeColorFunction('ff0000');}, false);
// the HTML form button to change the PNG color
var colorOfThePlacedPNG = "#000000";
var imagesToLoad = 0;
var imagesLoaded = 0;
function drawScreen() {
tempContext.fillStyle="#999999";
tempContext.fillRect(0,0,200,200); //color the whole temp canvas area grey....
tempContext.fillStyle="#2baae1";
tempContext.fillRect(30,30,140,140);//now draw a light blue rect inside....
tempContext.globalCompositeOperation="source-atop"; // now make everything drawn AFTERWARDS be clipped (masked) inside the blue rect
// when I comment out the above "global Comp Op"... everything draws to the main canvas normally...just not clipped(masked) however
tempContext.fillStyle="#f47e1f";
tempContext.fillRect(150,100,150,150);//SO heres an orange box intentionally clipped off the bottom right in the blue rect
tempContext.fillStyle="#d89bc5";
tempContext.fillRect(40,50,80,200);//AND heres a light purple rect intentionally clipped at the bottom of the blue rect
getTheImageData(); //draw PNG to another canvas, convert image data, put in tempContext
//tempContext.restore();//dont know if I need this
context.drawImage(tempCanvas, 0, 0);// and then FINALLY draw all to the main canvas
}
var loaded = function(){
imagesLoaded += 1;
if(imagesLoaded === imagesToLoad){
drawScreen();
}
}
var loadImage = function(url){
var image = new Image();
image.addEventListener("load",loaded);
imagesToLoad += 1;
image.src = url;
return image;
}
function changeColorFunction(e) {
colorOfThePlacedPNG = e;
drawScreen();
}
function getTheImageData(){
anotherContext.drawImage(testPNGimage, 0, 0);// draw to the third canvas(another canvas)
var imgData = anotherContext.getImageData(0, 0, 200, 200);
// how do i color it red? ....like #ff0000 ???
var i;
for (i = 0; i < imgData.data.length; i += 4) {
imgData.data[i] = 255 - imgData.data[i];
imgData.data[i+1] = 255 - imgData.data[i+1];
imgData.data[i+2] = 255 - imgData.data[i+2];
imgData.data[i+3] = 255;
}
tempContext.putImageData(imgData, 0, 0);
}
var testPNGimage = loadImage("test.png");// the PNG is just a 75X75px black squiggle drawn in pshop
}
You're overcomplicating things!
There is a clipping method built into canvas.
Use clipping instead of compositing and multiple canvases.
Do this and all new drawings will be clipped inside your 140x140 rect:
context.beginPath();
context.rect(30,30,140,140);
context.clip();
Here's a simplified redesign of your code:
Draw a grey rect filling the canvas.
Draw a blue 140x140 rect at [30,30].
Clip all new drawings into the blue rect with context.clip()
Draw a clipped orange rect.
Draw a clipped purple rect.
Unclip so new drawings will be visible anywhere on the canvas.
Draw the squiggle image (it's not clipped).
Use .getImageData to invert every pixel's color.
And a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var img=new Image();
img.crossOrigin='anonymous';
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/squiggle.png";
function start(){
//color the whole canvas area grey....
ctx.fillStyle="#999999";
ctx.fillRect(0,0,200,200);
//now draw a light blue rect inside....
ctx.fillStyle="#2baae1";
ctx.beginPath();
ctx.rect(30,30,140,140);
ctx.fill();
// save the unclipped context state
ctx.save();
// cause all new drawings to be clipped inside
// the blue 140x140 rect at [30,30]
ctx.clip();
//SO heres an orange box intentionally clipped off the bottom right in the blue rect
ctx.fillStyle="#f47e1f";
ctx.fillRect(150,100,150,150);
//AND heres a light purple rect intentionally clipped at the bottom of the blue rect
ctx.fillStyle="#d89bc5";
ctx.fillRect(40,50,80,200);
// restore the context state (releases clipping for new drawings)
ctx.restore();
// draw the squiggley line image -- it's not clipped in the blue rect
ctx.drawImage(img,0,0);
// invert the colors using getImageData
var imgData = ctx.getImageData(0, 0, 200, 200);
var i;
for (i = 0; i < imgData.data.length; i += 4) {
imgData.data[i] = 255 - imgData.data[i];
imgData.data[i+1] = 255 - imgData.data[i+1];
imgData.data[i+2] = 255 - imgData.data[i+2];
imgData.data[i+3] = 255;
}
ctx.putImageData(imgData, 0, 0);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=200 height=200></canvas>

How can I manipulate the slices of an image separately using canvas?

I am new to the "canvas" term in HTML. I sliced a picture in 2 halves, leaving a small gap between them. I want to rotate each half separately to create a half folding effect.
window.onload = function () {
var myCanv = document.getElementById("myCanvas");
var context = myCanv.getContext('2d');
var myImage = document.getElementById("myImage");
context.drawImage(myImage, 0, 0, myImage.width / 2, myImage.height, 0, 0, myCanv.width / 2, myCanv.height);
context.drawImage(myImage, (myImage.width / 2) + 1, 0, myImage.width / 2, myImage.height, (myCanv.width / 2) + 0.5, 0, myCanv.width / 2, myCanv.height);
};
Is there any way that I can do this using canvas? I appreciate any help. :)
Here is a fiddle. I know it's not much but any hint is useful.
You probably want to skew the 2 halves of the image rather than rotate them.
To skew you can use context.transform or context.setTransform.
Example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var skewLeft=.1;
var skewRight=-.1;
var cx=100;
var y=30;
var iw,ih;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/cars.jpg";
function start(){
iw=img.width;
ih=img.height;
draw();
}
function draw(){
// fill the canvas background with gray
ctx.fillStyle='gray';
ctx.fillRect(0,0,cw,ch);
ctx.fillStyle='black'
// draw the left skewed page with a stroked border
ctx.setTransform(1,skewLeft,0,1,cx,0);
ctx.drawImage(img,0,0,iw/2,ih,-iw/2,y,iw/2,ih);
ctx.strokeRect(-iw/2,y,iw/2,ih);
ctx.setTransform(1,0,0,1,0,0);
// draw the right skewed page with a stroked border
ctx.setTransform(1,skewRight,0,1,cx,0);
ctx.drawImage(img,iw/2,0,iw/2,ih,0,y,iw/2,ih);
ctx.strokeRect(0,y,iw/2,ih);
ctx.setTransform(1,0,0,1,0,0);
}
body{ background-color:white; }
img,#canvas{border:1px solid red;}
<h4>Original Image (left) and<br>Image halved, skewed & bordered to look like a fold</h4>
<img src='https://dl.dropboxusercontent.com/u/139992952/multple/cars.jpg'>
<canvas id="canvas" width=225 height=150></canvas>

How to rotate 2 rectangles in a canvas at the same time?

I know that I can use the "translate-rotate-translate back-fillrect" procedure to rotate one single rectangle.
However, what if I want to rotate them both at the same time and have a timeinterval() to make them rotate automatically every second?
I tried to save and restore each time I try to draw, but it didn't work.
You appear to be on the right track!
Save
Translate(rectX,rectY)
Rotate
fillRect(-rectWidth/2,-rectHeight/2,rectWidth,rectHeight) // draws the rect w/ center rotation
Restore
For multiple Rectangles
If you define some rectangle objects like this:
var rects=[];
rects.push({x:50,y:50,w:50,h:35,color:'green',angle:0});
rects.push({x:150,y:120,w:30,h:20,color:'blue',angle:0});
Then you can put them in an animation frame like this:
requestAnimationFrame(animate);
function animate(time){
// call for another loop in the animation
requestAnimationFrame(animate);
// clear canvas and redraw all rects
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<rects.length;i++){
// draw this rect at its specified angle
var rect=rects[i];
ctx.save();
ctx.translate(rect.x+rect.w/2,rect.y+rect.h/2);
ctx.rotate(rect.angle);
ctx.fillStyle=rect.color;
ctx.fillRect(-rect.w/2,-rect.h/2,rect.w,rect.h);
ctx.restore();
// increase this rect's angle for next time
rect.angle+=(Math.PI*2)/60;
}
}
requestAnimationFrame loops at about 60fps so if you increment the rect's angle in each loop by (Math.PI*2)/60 you will rotate the rect about one full turn every second.
Example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var rects=[];
rects.push({x:50,y:50,w:50,h:35,color:'green',angle:0});
rects.push({x:150,y:120,w:30,h:20,color:'blue',angle:0});
requestAnimationFrame(animate);
function animate(time){
// call for another loop in the animation
requestAnimationFrame(animate);
// clear canvas and redraw all rects
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<rects.length;i++){
// draw this rect at its specified angle
var rect=rects[i];
ctx.save();
ctx.translate(rect.x+rect.w/2,rect.y+rect.h/2);
ctx.rotate(rect.angle);
ctx.fillStyle=rect.color;
ctx.fillRect(-rect.w/2,-rect.h/2,rect.w,rect.h);
// orientation symbol
ctx.fillStyle='red';
ctx.fillRect(-rect.w/2,-rect.h/2,5,5)
ctx.restore();
// increase this rect's angle for next time
rect.angle+=(Math.PI*2)/60;
}
}
function drawRect(rect){
ctx.save();
ctx.translate(rect.x+rect.w/2,rect.y+rect.h/2);
ctx.rotate(rect.angle);
ctx.fillStyle=rect.color;
ctx.fillRect(-rect.w/2,-rect.h/2,rect.w,rect.h);
ctx.restore();
rect.angle+=deltaAngle;
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

drawing over an Image in HTML5 Canvas while preserving the image

In HTML5 Canvas, what's the simplest way to draw and move a line over an Image (already on the canvas), preserving the image underneath? (e.g. have a vertical line track the mouse X position)
My current canvas:
$(document).ready(function() {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 0,0);
}
imageObj.src = "http://example.com/some_image.png";
$('#myCanvas').click(doSomething);
});
You will have to do most of the ground-work with canvas which in this case you will have to implement the functionality to move the line and then redraw everything.
The steps can be:
Keep the line as an object which can self-render (method on the object)
Listen to mousemove (in this case) in order to move the line
For each move, redraw background (image) then render the line at its new position
You can redraw the background as a whole or you can optimize it to just draw over the last line.
Here is some example code of this and a live demo here:
var canvas = document.getElementById('demo'), /// canvas element
ctx = canvas.getContext('2d'), /// context
line = new Line(ctx), /// our custom line object
img = new Image; /// the image for bg
ctx.strokeStyle = '#fff'; /// white line for demo
/// start image loading, when done draw and setup
img.onload = start;
img.src = 'http://i.imgur.com/O712qpO.jpg';
function start() {
/// initial draw of image
ctx.drawImage(img, 0, 0, demo.width, demo.height);
/// listen to mouse move (or use jQuery on('mousemove') instead)
canvas.onmousemove = updateLine;
}
Now all we need to do is to have a mechnism to update the background and the line for each move:
/// updates the line on each mouse move
function updateLine(e) {
/// correct mouse position so it's relative to canvas
var r = canvas.getBoundingClientRect(),
x = e.clientX - r.left,
y = e.clientY - r.top;
/// draw background image to clear previous line
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
/// update line object and draw it
line.x1 = x;
line.y1 = 0;
line.x2 = x;
line.y2 = canvas.height;
line.draw();
}
The custom line object is in this demo very simple:
/// This lets us define a custom line object which self-draws
function Line(ctx) {
var me = this;
this.x1 = 0;
this.x2 = 0;
this.y1 = 0;
this.y2 = 0;
/// call this method to update line
this.draw = function() {
ctx.beginPath();
ctx.moveTo(me.x1, me.y1);
ctx.lineTo(me.x2, me.y2);
ctx.stroke();
}
}
If you are not gonna do anything specific with the image itself you can also set it as a background-image using CSS. You will still need to clear the canvas before redrawing the line though.
May this is not an actual answer, just in case you need it (in the future). Working with canvas would be better (and easier) with some library. I have tried EaselJS of CreateJS and find myself loving it.
You can have a look at it EaselJS
(I have done an example allow drawing and dragging image using EaselJS long time before)
You can get your "crosshairs" by listening to mousemove events and then:
clear the canvas
draw the image
draw your line at the mouse position
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/jEc7N/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.lineWidth=2;
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var img=new Image();
img.onload=function(){
canvas.width=img.width;
canvas.height=img.height;
ctx.drawImage(img,0,0);
}
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/KoolAidMan.png";
function handleMouseMove(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(img,0,0);
ctx.beginPath();
ctx.moveTo(mouseX,0);
ctx.lineTo(mouseX,canvas.height);
ctx.moveTo(0,mouseY);
ctx.lineTo(canvas.width,mouseY);
ctx.stroke();
}
$("#canvas").mousemove(function(e){handleMouseMove(e);});
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
or just use 2 layers:
background layer has image and do not change,
top layer has line, what you can clear and redraw it lots of time without affecting background layer.

Categories