canvas transform vertically [duplicate] - javascript

This question already has answers here:
HTML Canvas: How to draw a flipped/mirrored image?
(4 answers)
Closed 4 years ago.
I am a beginner on canvas and I have a question:
I have a background that I don’t want to change but I would like to switch a picture vertically (upside down).
I would like to be able to turn my character (a turtle) upside down at some points during my animation
Can Somebody help me, please
This is a part of my code
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var imgbackground = new Image();
img.src = 'BG.png';
ctx.drawImage(img, 0, 0, 457, 542);
var turtle = new Image(); // My character
turtle.src = 'turtle.png';
ctx.drawImage(turtle, 0, 0, 457, 542);
function gameTime (){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imgbackground, 0, 0, 900, 500);
ctx.drawImage(turtle, 0, 0, 900, 500);
window.requestAnimationFrame(gameTime)
}
window.requestAnimationFrame(gameTime)

So you can use scale(1, -1) to flip something vertically:
var canvas = document.getElementById('myCanvas')
var context = canvas.getContext('2d');
context.scale(1, -1);
The important part being the .scale which is flipping the Y value with -1,
context here would be the element, so change that to your required element.
so try turtle.scale(1, -1)
- To flip your lovely turtle.
You can also flip things horizontally with (-1, 1) - affecting the x value.

Related

HTML Canvas - Adjust opacity of multiple elements as a layer [duplicate]

This question already has an answer here:
Is there a way to disable color mixing/overlapping in html canvas
(1 answer)
Closed 7 months ago.
I'm trying to find a way to draw multiple elements onto an HTML canvas, then adjust all of their opacities at once. For example, this codepen example draws two overlapping rectangles with globalAlpha set to 0.5, so they're semi-transparent.
Here's what I see:
Here's what I want to see:
In other words, I want to draw some set of elements, then adjust their alpha/opacity all at once. In the example above, I want the overlapping section of blue & red to appear as just blue, since the blue rectangle was drawn 2nd.
I want this solution to apply to images, shapes, any canvas drawings really.
Does anyone know how to accomplish this using HTML canvas?
you must decompose the process
1- create de canvas with all draw ( alpha 100%)
2 set aside the flattened drawings
3 clear the canvas
4 fetch the picture and add it to the canvas
5 set alpha to 50%
6 add the tmp flattened drawings with alpha
const cnv = document.createElement("canvas");
cnv.width = 300;
cnv.height = 300;
const ctx = cnv.getContext("2d");
document.body.appendChild(cnv);
// Draw red rectangle
ctx.fillStyle = "#f00";
ctx.fillRect(20, 20, cnv.width - 140, cnv.height - 60);
// Draw blue rectangle
ctx.fillStyle = "#00f";
ctx.fillRect(100, 40, cnv.width - 140, cnv.height - 60);
//aside the draw in flatten layer
let tmp = new Image();
tmp.src = cnv.toDataURL();
// clear the canvas
ctx.clearRect(0, 0, cnv.width, cnv.height);
// apply bg
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, cnv.width, cnv.height);
const image = new Image(); // Using optional size for image
image.src = "https://img.photographyblog.com/reviews/kodak_pixpro_fz201/photos/kodak_pixpro_fz201_01.jpg";
image.onload = () => {
// Draw background image
ctx.drawImage(image, 0, 0);
ctx.drawImage(image, 0, 0, cnv.width, cnv.width);
// Set alpha to 0.5
ctx.globalAlpha = 0.5;
//overlay with the tmp flatten img with 50%
ctx.drawImage(tmp, 0, 0, cnv.width, cnv.width);
}

How can I remove/crop angle from corners of some image [duplicate]

This question already has answers here:
How to draw a rounded rectangle using HTML Canvas?
(15 answers)
Closed 7 months ago.
I'm trying to crop an image to make it fit on my margin. One example of what I'm trying to achieve.
The original image:
After the crop:
I already managed to crop the rectagle, but I have no idea how I can remove the corners. I tried with ctx.arc(), but I'm kinda confused with the values that I should use for x, y, radius and angle. The border-radius that I'm using depends on the screen size, but I've the value.
Use clip() with a Path2D and arcTo(). You will have to figure out your specific values which can be done with a little math (or trial and error). Be sure to draw you image after you clip()
arcTo()
clip()
let canvas = document.getElementById('canvas')
let ctx = canvas.getContext('2d')
canvas.width = 400;
canvas.height = 400;
let border = new Path2D();
border.arcTo(canvas.width, 0, canvas.width, 20, 50);
border.arcTo(canvas.width, canvas.height, 0, canvas.height, 50);
border.arcTo(0, canvas.height, 0, 20, 50);
border.arcTo(0, 0, 20, 0, 50);
ctx.clip(border);
function draw() {
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
draw()
<canvas id="canvas"></canvas>

Multiple masked images on top of each other using HTML canvas

I'm pretty new to canvas and haven't worked with it before but I thought it would be a good fit for the following task. While working on it I got doubts and I still don't know if the task is even possible to implement using canvas.
Exemplary graphic of the masks and images and the result that I want to achieve (and the actual results that I got).
The outlines are just there to better illustrate the images
dimensions.
The masks are SVG images which are preloaded using promises before
they are drawn and they change per iteration. So on the first
iteration it's mask A for image 1 and on the second iteration mask
B for image 2.
Simplified pseudo code example:
const items = [1, 2];
for (let i = 0; i < items.length; i++) {
ctx.drawImage(preloadedMask[i], x, y, canvasWidth, canvasHeight);
ctx.globalCompositeOperation = 'source-in';
img[i] = new Image();
img[i].onload = () => {
ctx.drawImage(img[i], 0, 0, canvasWidth, canvasHeight);
ctx.globalCompositeOperation = 'source-over';
//ctx.globalCompositeOperation = 'source-out';
};
img[i].src = `images/${i+1}.jpg`;
}
When I remove the globalCompositeOperation and the images the masks are perfectly drawn next to each other like I expected.
But as soon as I add a globalCompositeOperation it gets complicated and I am super confused to be honest.
I tried every possible globalCompositeOperation value in the onload callback - but it doesn't change much. I think I have to change the globalCompositeOperation after the mask is drawn for each iteration to a different value - but I am out of ideas.
Is there any way to achieve my desired output as described in the graphic or should I ditch canvas for this task?
What you're trying to achieve isn't that easy unfortunately - at least if you're using SVGs which are treated as images and directly drawn to the canvas.
Suppose we have the following svg masks and images
If we take the first mask and the first image and use the following code:
context.drawImage(maskA,0,0,width,height);
context.globalCompositeOperation = "source-in";
context.drawImage(imageA,0,0,width,height);
we get the desired output:
If we repeat the process and do the same for the second mask:
context.drawImage(maskB,0,0,width,height);
context.globalCompositeOperation = "source-in";
context.drawImage(imageB,0,0,width,height);
we'll just see an empty canvas. Why? We set globalCompositeOperation to 'source-in' and the previous canvas and the second mask (maskB) don't have any overlapping regions. That means we're effectively erasing the canvas.
If we try to compensate and either save/restore the context or reset globalCompositeOperation to it's initial state
context.save();
context.drawImage(maskA,0,0,width,height);
context.globalCompositeOperation = "source-in";
context.drawImage(imageA,0,0,width,height);
context.restore();
context.drawImage(maskB,0,0,width,height);
context.globalCompositeOperation = "source-in";
context.drawImage(imageB,0,0,width,height);
we still don't succeed:
So the trick here is this:
make sure both the svgs and images to be masked are fully loaded
create a new empty canvas the size of your target canvas
draw the first mask onto the new canvas
set it's globalCompositeOperation to 'source-in'
draw the first image onto the new canvas
draw the new canvas to the target canvas
erase the new canvas and repeat the previous steps to compose your final image
Here's an example (just click 'Run code snippet'):
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
let imagesLoaded = 0;
let imageA = document.getElementById("imageA");
let imageB = document.getElementById("imageB");
let width = canvas.width;
let height = canvas.height;
function loaded() {
imagesLoaded++;
if (imagesLoaded == 4) {
let tempCanvas = document.createElement("canvas");
let tempContext = tempCanvas.getContext("2d");
tempCanvas.width = width;
tempCanvas.height = height;
tempContext.save();
tempContext.drawImage(document.getElementById("semiCircleA"), 0, 0, width, height);
tempContext.globalCompositeOperation = "source-in";
tempContext.drawImage(imageA, 0, 0, width, 160);
ctx.drawImage(tempCanvas, 0, 0, width, height);
tempContext.restore();
tempContext.clearRect(0, 0, width, height);
tempContext.drawImage(document.getElementById("semiCircleB"), 0, 0, width, height);
tempContext.globalCompositeOperation = "source-in";
tempContext.drawImage(imageB, 0, 0, width, height);
ctx.drawImage(tempCanvas, 0, 0, width, height);
}
}
document.getElementById("semiCircleA").onload = loaded;
document.getElementById("semiCircleB").onload = loaded;
imageA.onload = loaded;
imageA.src = "https://picsum.photos/id/237/160/160";
imageB.onload = loaded;
imageB.src = "https://picsum.photos/id/137/160/160";
<h1>Final Canvas</h1>
<canvas id="canvas" width=160 height=160>
</canvas>
<br>
<h1>Sources</h1>
<img id="semiCircleA" src='data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="160px" height="160px">
<path d="M80,0 A80,80 0 0,0 80,160"/>
</svg>'>
<img id="semiCircleB" src='data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="160px" height="160px">
<path d="M80,0 A80,80 0 0,1 80,160"/>
</svg>'>
<img id="imageA">
<img id="imageB">
A canvas can be a layer
The canvas like any element is easy to create and can be treated like an image, or if you are familiar with photoshop, a canvas can be a layer.
To create a blank canvas
// Returns the renderable image (canvas)
function CreateImage(width, height) {
return Object.assign(document.createElement("canvas"), {width, height});
}
To copy a canvas or image like object
// Image can be any image like element including canvas. Returns the renderable image
function CopyImage(img, width = img.width, height = img.height, smooth = true) {
const can = createImage(width, height});
can.ctx = can.getContext("2d");
can.ctx.imageSmoothingEnabled = smooth;
can.ctx.drawImage(img, 0, 0, width, height);
return can;
}
Loading
Never load images in a render loop. The image onload event will not respect the order you assign the src. Thus the rendering of images in onload will not always be in the order you wish.
Load all images and wait before rendering.
An example of loading a set of images. The function loadImages returns a promise that will resolve when all images have loaded.
const images = {
maskA: "imageUrl",
maskB: "imageUrl",
imgA: "imageUrl",
imgB: "imageUrl",
};
function loadImages(imgList, data) {
return new Promise((done, loadingError) => {
var count = 0;
const imgs = Object.entries();
for (const [name, src] of imgs) {
imgList[name] = new Image;
imgList[name].src = src;
count ++;
imgList[name].addEventListener("load", () => {
count--;
if (count === 0) { done({imgs: imgList, data}) }
}, {once, true)
);
imgList[name].addEventListener("error", () => {
for (const [name, src] of imgs) { imgList[name] = src }
loadingError(new Error("Could not load all images"));
}, {once, true)
);
}
});
}
Rendering
It is best to create functions to do repeated tasks. One task you are repeating is masking, the following function uses a canvas as a destination, an image, and a mask
function maskImage(ctx, img, mask, x = 0, y = 0, w = ctx.canvas.height, h = ctx.canvas.width, clear = true) {
ctx.globalCompositeOperation = "source-over";
clear && ctx.clearRect(0, 0, ctx.canvas.height, ctx.canvas.width);
ctx.drawImage(img, x, y, w, h);
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(mask, 0, 0, w, h);
return ctx.canvas; // return the renderable image
}
Once you have some utilities set up to help coordinate the loading and rendering you can composite your finial result
// assumes ctx is the context to render to
loadImages(images, {ctx}).then(({imgs, {ctx}} => {
const w = ctx.canvas.width, h = ctx.canvas.height;
ctx.clearRect(0, 0, w, h);
const layer = copyImage(ctx.canvas);
ctx.drawImage(maskImage(layer.ctx, imgs.imgA, imgs.maskA), 0, 0, w, h);
ctx.drawImage(maskImage(layer.ctx, imgs.imgB, imgs.maskB), 0, 0, w, h);
// if you no longer need the images always remove them from memory to avoid hogging
// client's resources.
imgs = {}; // de-reference images so that GC can clean up.
}
You can now layer as many masked images as needed. Because functions where created for each sub task it is easy to create more complicated rendering without needing to write verbose and repeated code, in both this project and future projects.

Fast image scale down?

I'm writing a "TV filter" (you know the kind, RGB bars as it zooms in), for a video file and I've been having a look at some ways of shrinking the image that retains as much detail as possible.
For testing I'm drawing the sampled image back to the screen to see the quality - in the actual filter, I'll just be sampling pixels and getting the RGB values of the resultant computed color.
I've tried three, and the Hermite filter looks good, but compared to the speed "hardware" nearest neighbour version, it's not going to be suitable for processing video.
Is there any "tricks" in JavaScript that can be used to get accelerated image shrinking like 2, but with a quality like 1 or 3?
1: Brute force: http://codepen.io/SarahC/pen/VpvWvb?editors=1010
2: Internal nearest neighbour: http://codepen.io/SarahC/pen/ryeQgN?editors=1010
3: Hermite filter: http://codepen.io/SarahC/pen/ryMNWZ?editors=1010
Here's the "hardware"? version:
function processResize(percent) {
var size = percent * 0.01;
var sw = canvas.width * size;
var sh = canvas.height * size;
ctx.drawImage(canvas2, 0, 0, sw, sh);
ctx.drawImage(canvas, 0, 0, sw, sh, 0, 0, w, h);
}
I am not entirely sure from the description what you try to achieve, but from the codepens it seems as you try to create a mosaic effect.
You can use the built-in interpolation setting of the canvas context to use nearest-neighbor by turning image smoothing off, then draw the image to a small size representing how many "blocks" you want. Then draw back that version to full size again:
// blocks = initial number of pixels (video aspect is usually 16:9 so you may want
// to calculate a separate values for height.
var blocks = 24;
// draw initial size representing "blocks"
ctx.drawImage(video, 0, 0, blocks, blocks);
// turn off image smoothing (see below for prefixing)
// This uses nearest neighbor
ctx.imageSmoothingEnabled = false;
// enlarge the mosaic back to full size
ctx.drawImage(c, 0, 0, blocks, blocks, 0, 0, c.width, c.height);
Video Example
(the video may take a few seconds to load...)
var ctx = null;
var blocks = 24;
var video = document.createElement("video");
video.preload = "auto"; video.muted = video.autoplay = video.loop = true;
video.oncanplay = function() { // initialize for demo
if (!ctx) {
c.width = this.videoWidth;
c.height = this.videoHeight;
ctx = c.getContext("2d");
document.querySelector("input").oninput = function() {blocks = +this.value};
requestAnimationFrame(loop);
}
}
video.src = "//media.w3.org/2010/05/sintel/trailer.mp4";
function smoothing(state) {
ctx.oImageSmoothingEnabled = ctx.msImageSmoothingEnabled =
ctx.mozImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled =
ctx.imageSmoothingEnabled = state;
}
function loop() {
smoothing(true); // improve quality of first step
ctx.drawImage(video, 0, 0, blocks, blocks);
smoothing(false); // mosaic step
ctx.drawImage(c, 0, 0, blocks, blocks, 0, 0, c.width, c.height);
// loop and throttle to 30 fps
requestAnimationFrame(function() {requestAnimationFrame(loop)});
}
<label>Blocks: <input type=range min=8 max=128 value=24></label><br>
<canvas id=c></canvas>

How can I use a background image on a canvas?

I found this code
http://jsfiddle.net/dtrooper/AceJJ/
and I'm trying to add a background image to the canvas (so it won't be black).
I've added these lines to the $(document).ready function
var background = new Image();
background.src = url("img/header.jpg");
But all I get is Uncaught SyntaxError: Unexpected token ILLEGAL
I also want to have text on top of the background (saying something like "Welcome to the new year" + NewLine + "Be safe and good luck"
Thank You
In the loop function change this:
// clear canvas
context.fillStyle = "rgba(0, 0, 0, 0.05)";
context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
To this:
context.save();
if (first) { // only the first time you draw the full image
first = false;
context.globalAlpha = 1;
} else {
context.globalAlpha = 0.2; // <--- PLAY WITH THIS FOR BETTER EFFECT
}
context.drawImage(img, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
context.restore()
// clear canvas
//context.fillStyle = "rgba(0, 0, 0, 0.05)";
//context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
And out of the loop function add this:
var img = new Image();
var first = true;
img.src = "YOUR_IMAGE_URL";
Is not perfect, but from here you can find your solution.
The example:
http://jsfiddle.net/AceJJ/1748/
jsFiddle : https://jsfiddle.net/CanvasCode/6ju8acro/1/
javascript
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = new Image();
img.src = "http://imagesci.com/img/2013/03/cute-christmas-dogs-8313-hd-wallpapers.jpg";
img.onload = function() {
ctx.drawImage(img, 0, 0);
ctx.font = '40pt Calibri';
ctx.fillStyle = 'red';
ctx.fillText("Welcome to the new year", 250, 100);
ctx.fillText("Be safe and good luck", 350, 600);
}
You have to create a new Image variable and then access its property src which is the source of the image. So you first create a new Image provide a source. Then we must first wait for the image to load. Once the image has loaded we render it to the canvas via drawImage, then we go ahead and draw some text using the fillText function.
You will have to work out how to centre the text correctly and change the image but the code I have provided should get you started :)
Updated
jsFiddle : http://jsfiddle.net/CanvasCode/AceJJ/1743/
// clear canvas
// Render background and text first
context.drawImage(img, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
context.font = '40pt Calibri';
context.fillStyle = 'red';
context.fillText("Welcome to the new year", 150, 100);
context.fillText("Be safe and good luck", 250, 600);
//context.fillStyle = "rgba(0, 0, 0, 0.05)";
//context.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
Just render the image and text in the loop function, however as I stated above you will need to work out the best way to render the image and text
Another Update
jsFiddle : http://jsfiddle.net/CanvasCode/AceJJ/1756/
To draw the firework trails over the image, just create particles when the rocket is rendered like so
// ... near the end of the Rocket.prototype.render function
c.fill();
c.restore();
var particle = new Particle(this.pos);
particle.size = 10;
particle.gravity = 0.2;
particle.resistance = 0.92;
particle.shrink = Math.random() * 0.05 + 0.93;
particle.flick = true;
particle.color = this.explosionColor;
particles.push(particle);
};
Try this :
var img = new Image();
img.src = "http://imgurl.com";
url() is used in CSS.

Categories

Resources