Transfer only a Part of Canvas to a New Canvas - javascript

I have a canvas with an id of cnv.
<canvas id='cnv'></canvas>
The dark rectangle is the whole canvas. I want to create another canvas that contains only the white region in the old canvas. How will I transfer a part of a canvas to a new canvas?
var cnv = document.getElementById('cnv');
I don't know what to do next in my code above.

Assuming you have the region specificed in x, y, width and height, you can do:
function regionToCanvas(canvas, x, y, w, h) {
var c = document.createElement("canvas"), // create new canvas
ctx = c.getContext("2d"); // context for new canvas
c.width = w; // set size = w/h
c.height = h;
ctx.drawImage(canvas, x, y, w, h, 0, 0, w, h); // draw in region at (0,0)
return c; // return canvas
}
Then call, example:
var newCanvas = regionToCanvas(cnv, x, y, width, height);
document.body.appendChild(newCanvas); // add to DOM

Related

Adding outer stroke to colliding canvas rectangles

I'm adding multiple rectangles in canvas which could collide with each other. The outer stroke should be displayed on the outer part of both rectangles or the rectangle shapes should be merged in to one producing the expected result.
See picture bellow
It has to be cut because it will display the content under the canvas. See live example with background image: https://jsfiddle.net/0qpgf5un/
In the code example bellow rectangles are being added on top of each other as you can see in the first example of the picture.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var offsetX = 150;
var offsetY = 150;
var w = 200;
var h = 100;
ctx.fillStyle = "red";
ctx.rect(0, 0, 600, 600);
ctx.fill();
ctx.clearRect(offsetX,offsetY, w, h);
ctx.strokeRect(offsetX, offsetY, w, h);
ctx.clearRect(offsetX-50,offsetY+50, w, h);
ctx.strokeRect(offsetX-50, offsetY+50, w, h);
Is there ways to achieve it without writing complex calculations of each path, since the collision of rectangles can be unintentional and diverse ?
Edit:
What I am trying to achieve is a similar functionality like in youtube's feedback form where when editing screenshot you can highlight items and the border then is merged.
Just add one more clearRect() (the first one)
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var offsetX = 150;
var offsetY = 150;
var w = 200;
var h = 100;
ctx.fillStyle = "red";
ctx.rect(0, 0, 600, 600);
ctx.fill();
ctx.clearRect(offsetX,offsetY, w, h);
ctx.strokeRect(offsetX, offsetY, w, h);
ctx.clearRect(offsetX-50,offsetY+50, w, h);
ctx.strokeRect(offsetX-50, offsetY+50, w, h);
ctx.clearRect(offsetX,offsetY, w, h);
https://jsfiddle.net/kt3yjhpc/
You can skip clearing the first rectangle and then clear it after you stroke the second one.
The clearPrev function will clear the area inside the strokes of the initial rectangle.
let canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
offsetX = 70,
offsetY = 20,
w = 200,
h = 100,
strokeWidth = 5;
ctx.fillStyle = '#F00'
ctx.rect(0, 0, 600, 600);
ctx.fill();
ctx.strokeStyle = '#0FF';
ctx.lineWidth = strokeWidth;
//ctx.clearRect(offsetX, offsetY, w, h); <-- Do not need to do this, if we clear below...
ctx.strokeRect(offsetX, offsetY, w, h);
ctx.clearRect(offsetX - 50, offsetY + 50, w, h);
ctx.strokeRect(offsetX - 50, offsetY + 50, w, h);
clearPrev(ctx, offsetX, offsetY, w, h); // Clear previous
function clearPrev(ctx, x, y, w, h) {
let startOffset = Math.round(ctx.lineWidth / 2) - 1,
endOffset = strokeWidth - 1;
ctx.clearRect(x + startOffset, y + startOffset, w - endOffset, h - endOffset);
}
<canvas id="canvas" width="290" height="190"></canvas>
When you want to clear the canvas with complex shapes, forget about clearRect, it's not the only one able to produce transparent pixels.
Instead, have a look at compositing.
So your shape is really border-line, but I think you'll benefit from using this already:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var offsetX = 150;
var offsetY = 150;
var w = 200;
var h = 100;
ctx.lineWidth = 2;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 600, 600);
// declare our complex shape as a single sub-path
ctx.beginPath()
ctx.rect(offsetX,offsetY, w, h);
ctx.rect(offsetX-50, offsetY+50, w, h);
// now we can paint it
// first the stroke, because we want to erase what's inside the fill-area
ctx.stroke();
// now to erase, we switch to destination-out compositing mode
ctx.globalCompositeOperation = 'destination-out';
// fill the inner path
ctx.fill();
// we're done
// If you wish to go back to normal mode later
ctx.globalCompositeOperation = 'source-over';
body { background: linear-gradient(blue,yellow); }
<canvas id="canvas" width="600" height="600"></canvas>

Making Canvas Sprites Centered on Coordinates

I am working on a Canvas Game on HTML5 Canvas with Vanilla JS.
For some reason I am noticing that when I set the player sprite to be drawn in at on the x-axis at x=0, the player appears indented to the right.
(this appears to be disrupting my collision detection)
I have the same issue with other sprites I have generated with the piskel app. Another sprite I used from another creator didn't have this issue.
Does anyone have any suggestions?
Here is a link to my game: http://zcbuhler.github.io/spaceDrift
The player should be at 0 on the x-axis for the starting point, but as you can see appears to be tabbed over.
General purpose sprite rendering
I render sprites with the following function.
// assumes ctx is scoped and is the rendering 2d context
function drawSprite(img, x, y, scale, rotate, alpha){
var w = img.width;
var h = img.height;
ctx.setTransform(scale, 0, 0 ,scale, x, y);
ctx.rotate(rotate);
ctx.globalAlpha = alpha;
ctx.drawImage(img, 0, 0, w, h, -w/2,-h/2, w, h);
}
It draws the sprite with its center at x,y. It is scaled and and rotated and its alpha is set. On a average laptop and on firefox it can do 2000+ sprites in realtime. On chrome its about 1000+
To set the center point use
// assumes ctx is scoped and is the rendering 2d context
function drawSpriteCenter(img, x, y, cx, cy, scale, rotate, alpha){
var w = img.width;
var h = img.height;
ctx.setTransform(scale, 0, 0 ,scale, x, y);
ctx.rotate(rotate);
ctx.globalAlpha = alpha;
ctx.drawImage(img, 0, 0, w, h, -cx,-cy, w, h);
}
where cx, and cy s the center point of the sprite. (the point around which it rotates)
To draw a sprite with a center cx,cy at x,y and a scale for x and y, rotated with alpha.
// assumes ctx is scoped and is the rendering 2d context
function drawSpriteFull(img, x, y, cx, cy, scaleX, scaleY, rotate, alpha){
var w = img.width;
var h = img.height;
ctx.setTransform(scaleX, 0, 0 ,scaleY, x, y);
ctx.rotate(rotate);
ctx.globalAlpha = alpha;
ctx.drawImage(img, 0, 0, w, h, -cx, -cy, w, h);
}
The functions modify the current transform and alpha. To reset the canvas state once you are done rendering the sprites you can use
function resetCtx(){
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.globalAlpha = 1;
}
You should be able to correct the player's position like #Bangkokian pointed out.
Simply wrap that in a function like so:
function positionPlayer(x, y) {
player.pos = [x -= (player.w / 2), y];
}
or even make that a method of the players object.
The collision detection could be solved similarly by altering the 'checkCollision' function to something like:
var checkCollision = function(rect1, rect2) {
// debugger
// centers
var rect1CenteredPos = [rect1.pos[0] - (rect1.w / 2), rect1.pos[1] - (rect1.h / 2)];
var rect2CenteredPos = [rect2.pos[0] - (rect2.w / 2), rect2.pos[1] - (rect2.h / 2)];
// console.log(bulletsArray.length);
if (rect1CenteredPos[0] < rect2CenteredPos[0] + rect2.w &&
rect1CenteredPos[0] + rect1.w > rect2CenteredPos[0] &&
rect1CenteredPos[1] < rect2CenteredPos[1] + rect2.h &&
rect1.h + rect1CenteredPos[1] > rect2CenteredPos[1]) {
return true;
}
return false;
}

Re-fill area of erased area for canvas

I am trying to fill color in image using below code snippet for filling color on Image of canvas . Its successfully filling color in canvas. Now I am trying to erase filled color on touch of user using this code snippet for erasing color on Image of canvas . Its erasing color & setting transparent area on that touched position. Now I want to refill that area on user touch with colors but its not allowing me to color on that because of transparent pixels. So Is there any way to refill pixels with color Or Is there any other way to erase color from image of canvas ? Any reply will be appreciated.
code snippet for filling color on Image of canvas
var ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.crossOrigin = 'anonymous';
img.src = "https://dl.dropboxusercontent.com/s/1alt1303g9zpemd/UFBxY.png";
function draw(color) {
ctx.drawImage(img, 0, 0);
}
canvas.onclick = function(e){
var rect = canvas.getBoundingClientRect();
var x = e.clientX-rect.left,
y = e.clientY-rect.top;
ctx.globalCompositeOperation = 'source-atop';
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(x-5,y-5,10,0,2*Math.PI);
ctx.fill();
}
code snippet for erasing color on Image of canvas
(function() {
// Creates a new canvas element and appends it as a child
// to the parent element, and returns the reference to
// the newly created canvas element
function createCanvas(parent, width, height) {
var canvas = {};
canvas.node = document.createElement('canvas');
canvas.context = canvas.node.getContext('2d');
canvas.node.width = width || 100;
canvas.node.height = height || 100;
parent.appendChild(canvas.node);
return canvas;
}
function init(container, width, height, fillColor) {
var canvas = createCanvas(container, width, height);
var ctx = canvas.context;
// define a custom fillCircle method
ctx.fillCircle = function(x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
ctx.clearTo = function(fillColor) {
ctx.fillStyle = fillColor;
ctx.fillRect(0, 0, width, height);
};
ctx.clearTo(fillColor || "#ddd");
// bind mouse events
canvas.node.onmousemove = function(e) {
if (!canvas.isDrawing) {
return;
}
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
var radius = 10; // or whatever
var fillColor = '#ff0000';
ctx.globalCompositeOperation = 'destination-out';
ctx.fillCircle(x, y, radius, fillColor);
};
canvas.node.onmousedown = function(e) {
canvas.isDrawing = true;
};
canvas.node.onmouseup = function(e) {
canvas.isDrawing = false;
};
}
var container = document.getElementById('canvas');
init(container, 531, 438, '#ddd');
})();
Warning untested code!
// create a clipping region using your erasing rect's x,y,width,height
context.save();
context.beginPath();
context.rect(erasingRectX,erasingRectY,erasingRectWidth,erasingRectHeight);
context.clip();
// redraw the original image.
// the image will be redrawn only into the erasing rects boundary
context.drawImage(yourImage,0,0);
// compositing: new pixels draw only where overlapping existing pixels
context.globalCompositeOperation='source-in';
// fill with your new color
// only the existing (clipped redrawn image) pixels will be colored
context.fillStyle='red';
context.fillRect(0,0,canvas.width,canvas.height);
// undo the clipping region
context.restore();

How to embed string in a background image?

I am trying to make a scratch card looks like
Basically I have a gray layer to scratch and it will reveal the red image.
Now I have set up the canvas and ctx is for the gray layer, but the background is fixed.
I want to have the "12345" be a variable to embed in the background,
which means I can have different number in the background image.
Here is the function I have for the scratch card.
(addEventlistener is just used for scratching the gray layer)
function bodys(height,width){
var img = new Image();
var canvas = document.querySelector('canvas');
canvas.style.position = 'absolute';
img.addEventListener('load',function(e){
var ctx;
var w = width, h = height;
var offsetX = canvas.offsetLeft, offsetY = canvas.offsetTop;
var mousedown = false;
function layer(ctx){
ctx.fillStyle = 'gray';
ctx.fillRect(0, 0, w, h);
}
function eventDown{
...
}
canvas.width=w;
canvas.height=h;
canvas.style.backgroundImage='url('+img.src+')';//here is the background image I use
ctx=canvas.getContext('2d');
ctx.fillRect(0, 0, w, h);
layer(ctx);
ctx.globalCompositeOperation = 'destination-out';
canvas.addEventListener{
...
}
});

Canvas rotation doesn't work properly

I am having troubles trying to rotate a rectangle via JavascriptCanvas API.
Here is the code:
G = {};
// get canvas context
G.ctx = document.getElementById('canvas').getContext('2d');
var x = 200;
var y = 100;
var w = 30;
var h = 70;
G.ctx.fillRect(x, y, w, h);
// Why is this not working??
G.ctx.save();
G.ctx.translate(x, y);
G.ctx.rotate(30*(Math.PI/180));
G.ctx.fillRect(x, y, w, h);
G.ctx.restore();
The code only draws the first rectangle for some reason.
Here is the JSfiddle: http://jsfiddle.net/5YZbd/1/
Any clarification is really welcome!
I figured it out.
As soon as I translate the canvas to rectangle's x/y - its position should be referred to as 0/0, cause thats where the canvas origin is after the translation.
Here is the working code:
G.ctx.save();
G.ctx.translate(x, y);
G.ctx.rotate(30*(Math.PI/180));
G.ctx.fillRect(0, 0, w, h);
G.ctx.restore();

Categories

Resources