I been stuck on getting the waves to look just like I want. I'm trying to figure out how to get the base of the wave to be the color I need it. I can do my desired color but it blocks the background. I can not see anything behind it because I was using like a reflection. Maybe someone can figure it out cause I'm having difficulties getting it to work... I plan on making the wave drop and rise. Here is a link to the code pen: HERE
Here is where I have the vertical reflection:
var x = $.cx - $.length / 2 + $.length / $.count * i,
y = height + $.simplex.noise2D($.xoff, $.yoff) * amp + sway;
$.ctx[i === 0 ? 'moveTo' : 'lineTo'](x, y);
}
$.ctx.lineTo($.w, $.h); // -$.h - Vertically reflection
$.ctx.lineTo(0, $.h); // -$.h - Vertically reflection
$.ctx.closePath();
$.ctx.fillStyle = color;
if (comp) {
$.ctx.globalCompositeOperation = comp;
}
$.ctx.fill();
My desired look for the waves is below:
Here is what I got with a successful transparent top, just not the right coloring:
Your problem is that the screen blending of the three colors generates a solid white color, so all the bottom of your canvas becomes white.
Here I simplified a lot the situation, with just 3 rectangles. Your bottom of canvas is my central white square:
const c2 = canvas.cloneNode();
const ctx = canvas.getContext("2d");
ctx.globalCompositeOperation = 'screen';
ctx.fillStyle = '#fb0000';
ctx.fillRect(0,0,50,50);
ctx.fillStyle = "#00ff8e";
ctx.fillRect(12,12,50,50);
ctx.fillStyle = "#6F33FF";
ctx.fillRect(25,25,50,50);
body {
background: #CCC;
}
<canvas id="canvas"></canvas>
So what we need, is a way to make this central square transparent so that we can draw our background behind.
To do this, we will need to draw our shapes at least two times:
once in normal compositing mode, so that we get the full overlap.
once again as source-in compositing mode, so that we get only where all our shapes do overlap.
const ctx = canvas.getContext("2d");
function drawShapes(mode) {
ctx.globalCompositeOperation = mode;
ctx.fillStyle = '#fb0000';
ctx.fillRect(0,0,50,50);
ctx.fillStyle = "#00ff8e";
ctx.fillRect(12,12,50,50);
ctx.fillStyle = "#6F33FF";
ctx.fillRect(25,25,50,50);
}
drawShapes('screen');
drawShapes('source-in');
body {
background: #CCC;
}
<canvas id="canvas"></canvas>
Now we have our overlapping area, we will be able to use it as a cutting shape in a third operation. But to do it, we will need a second, off-screen canvas to perform the compositing of the two states:
const c2 = canvas.cloneNode();
const ctx = canvas.getContext("2d");
const ctx2 = c2.getContext("2d");
function drawShapes(ctx, comp) {
ctx.globalCompositeOperation = comp;
ctx.fillStyle = '#fb0000';
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = "#00ff8e";
ctx.fillRect(12, 12, 50, 50);
ctx.fillStyle = "#6F33FF";
ctx.fillRect(25, 25, 50, 50);
}
// first draw our screen, with unwanted white square
drawShapes(ctx, 'screen');
// draw it on the offscreen canvas
ctx2.drawImage(ctx.canvas, 0, 0)
// draw the shapes once again on the offscreen canvas to get the cutting shape
drawShapes(ctx2, 'source-in');
// cut the visible canvas
ctx.globalCompositeOperation = 'destination-out'
ctx.drawImage(ctx2.canvas, 0, 0);
body {
background: #CCC
}
<canvas id="canvas"></canvas>
And voilà, our white square is now transparent, we can draw whatever we want behind our scene using the destination-over composite operation.
Related
Is there a way how to fill circle with lines, in same way as it shown in the picture, without math computations for x1y1-x2y2 points on circle body? I mean, something like configuration for gradient or anything else?
Code taken from this mdn article
Result:
// Create a pattern, offscreen
const patternCanvas = document.createElement("canvas");
const patternContext = patternCanvas.getContext("2d");
// Give the pattern a width and height of 50
patternCanvas.width = 25;
patternCanvas.height = 25;
// Give the pattern a background color and draw an arc
patternContext.fillStyle = "white";
patternContext.strokeStyle = "green";
patternContext.lineWidth = 2
patternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
patternContext.moveTo(0, 0);
patternContext.lineTo(25, 25);
patternContext.stroke();
// Create our primary canvas and fill it with the pattern
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const pattern = ctx.createPattern(patternCanvas, "repeat");
ctx.fillStyle = pattern;
ctx.arc(50, 50, 50, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Add our primary canvas to the webpage
document.body.appendChild(canvas);
Without math
canvas {
background-image: url("https://i.stack.imgur.com/Q2svp.png");
background-repeat: no-repeat;
}
<canvas width="275.994" height="283.991"></canvas>
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);
}
I'm using canvas to create a dynamic Wheel of fortune. Any time I add a new item, it recalculates the spaces evenly. I was able to make it happen using canvas. I know that canvas.fillStyle = somecolor; sets the color I want to the slice that is being created.
I also wanted to put some text inside each slice. However, I can't figure a way to make canvas draw a text with a different color from the color of the slice. Is that even possible?
Here's what I'm doing
const drawSlice = useCallback((deg: number, color: string, text:string) => {
if (!canvasRef.current) {
return;
}
const canvas: HTMLCanvasElement = canvasRef.current;
const ctx = canvas.getContext("2d");
const width = canvas.width;
const center = width/2;
const sliceDeg = 360 / wheelItems.length;
if (ctx) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.fillText(text, center, deg2rad(deg));
ctx.moveTo(center, center);
ctx.arc(center, center, width / 2, deg2rad(deg), deg2rad(deg + sliceDeg));
ctx.lineTo(center, center);
ctx.fill();
}
},[wheelItems.length])
If I understand your problem correctly, you might want to change ctx.fillStyle between the moment you draw the text and the moment you draw the slice.
if (ctx) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.fillText(text, center, deg2rad(deg));
// here :
ctx.fillStyle = color2;
ctx.moveTo(center, center);
ctx.arc(center, center, width / 2, deg2rad(deg), deg2rad(deg + sliceDeg));
ctx.lineTo(center, center);
ctx.fill();
}
You can change ctx.fillStyle whenever you want, as many times as you want.
I'd like to give a sprite an outline when the character gets healed/damaged/whatever but I can't think of a way to code this using the 2d canvas. If it were possible, I'd think it would be a global composite operation, but I can't think of a way to achieve it with one of them.
I did find this stackoverflow answer that recommends creating a fatter, solid color version of the original and put the original on top of it. That would give it an outline, but it seems like a lot of extra work especially considering I'm using placeholder art. Is there an easier way?
This question is different from the one linked because this is specifically about the HTML5 2D canvas. It may have a solution not available to the other question.
For what it's worth, I don't mind if the outline creates a wider border or keeps the sprite the same size, I just want the outline look.
Just draw your original image in 8 position around the original image
Change composite mode to source-in and fill with the outline color
Change composite mode back to source-over and draw in the original image at correct location
This will create a clean sharp outline with equal border thickness on every side. It is not so suited for thick outlines however. Image drawing is fast, especially when image is not scaled so performance is not an issues unless you need to draw a bunch (which in that case you would cache the drawings or use a sprite-sheet anyways).
Example:
var ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.src = "http://i.stack.imgur.com/UFBxY.png";
function draw() {
var dArr = [-1,-1, 0,-1, 1,-1, -1,0, 1,0, -1,1, 0,1, 1,1], // offset array
s = 2, // scale
i = 0, // iterator
x = 5, // final position
y = 5;
// draw images at offsets from the array scaled by s
for(; i < dArr.length; i += 2)
ctx.drawImage(img, x + dArr[i]*s, y + dArr[i+1]*s);
// fill with color
ctx.globalCompositeOperation = "source-in";
ctx.fillStyle = "red";
ctx.fillRect(0,0,canvas.width, canvas.height);
// draw original image in normal mode
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(img, x, y);
}
<canvas id=canvas width=500 height=500></canvas>
Maybe it would be worth trying this :
• build a canvas 1.1 time bigger than the original sprite
• fill it with the outline color
• draw the sprite scaled by 1.1 on the canvas using destination-in globalCompositeOperation.
Then you have a bigger 'shadow' of your sprite in the outline color.
When you want to draw the outline :
• draw the 'shadow' (centered)
• draw your sprite within the shadow.
Depending on the convexity of your sprite, this will work more or less nicely, but i think it's worth trying since it avoids you doubling the number of input graphic files.
I just did a short try as proof-of-concept and it quite works :
http://jsbin.com/dogoroxelupo/1/edit?js,output
Before :
After :
html
<html>
<body>
<image src='http://www.gifwave.com/media/463554/cartoons-comics-video-games-sprites-scott-pilgrim-paul-robertson_200s.gif' id='spr'></image>
<canvas id='cv' width = 500 height= 500 ></canvas>
</body>
</html>
code
window.onload=function() {
var spr = document.getElementById('spr');
var margin = 4;
var gh = createGhost(spr, '#F80', margin);
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
var outlined = true;
setInterval(function() {
ctx.clearRect(0,0,cv.width, cv.height);
if (outlined)
ctx.drawImage(gh, 0, 0)
ctx.drawImage(spr, 0, 0)
outlined = !outlined;
}, 400);
}
function createGhost (img, color, margin) {
var cv= document.createElement('canvas');
cv.width = img.width+2*margin;
cv.height = img.height + 2*margin;
var ctx = cv.getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0,0, cv.width, cv.height);
ctx.save();
ctx.globalCompositeOperation = 'destination-in';
var scale = cv.width/spr.width;
ctx.scale(cv.width/spr.width, cv.height/spr.height);
ctx.drawImage(img, -margin, -margin);
ctx.restore();
return cv;
}
You could use strokeRect method to outline the sprite after drawing it. It should be asy if you know your sprite's dimensions...
I'm drawing simple text in HTML5 canvas using this:
context.fillStyle = "#FF0000"
context.font = "italic 20pt Arial";
context.fillText("sfddsfs", 50, 50);
Now I want to animate fade out of this text. How can that be done?
Edit: I'm aware there's currently no ready to use way to do this (at least I can't find anything). I'm a noob in graphic programming but want to learn, so any hint about where to start is appreciated.
Maybe something like putting the text in an own canvas and changing globalAlpha of the canvas...? But the background of the canvas would have to be transparent. And don't know about performance, have a lot of small labels appearing and dissapearing everywhere which need to be faded out.
It's easier if you use rgba() notation to set the fillStyle rather than the hexadecimal notation. Here's a working example (demo):
// Create a canvas element and append it to <body>
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
document.body.appendChild(canvas);
function fadeOut(text) {
var alpha = 1.0, // full opacity
interval = setInterval(function () {
canvas.width = canvas.width; // Clears the canvas
context.fillStyle = "rgba(255, 0, 0, " + alpha + ")";
context.font = "italic 20pt Arial";
context.fillText(text, 50, 50);
alpha = alpha - 0.05; // decrease opacity (fade out)
if (alpha < 0) {
canvas.width = canvas.width;
clearInterval(interval);
}
}, 50);
}
fadeOut('sfddsfs');
I think I got it. Forgot to mention that I have already a render loop and text objects which draw themselves on the canvas each frame.
So the solution is to add alpha variable to the text objects:
this.alpha = 1;
and each x frames or time reduce this a bit.
and in the render loop:
context.globalAlpha = textObject.alpha;
//draw the text
context.globalAlpha = 1;
There is no built-in solution to this. You have to do the animation(fade) by drawing each frame individually:
Set up some timing function that calculates the gradient between #FF0000 and the background color and redraws the text over and over until the background color is reached.