I would like to obtain a radial gradient effect on an image (alpha = 1 in the middle and transparent on the edges).
Do you have any ideeas on how I could do that?
If I'm not mistaking what you want to do is:
Draw the image
Draw a radial gradient over it, where the borders are transparent and the middle is opaque and using the globalCompositeOperation setting on the context to blend the transparency gradient with the image.
You can rather easily translate this into code: http://jsfiddle.net/W8Ywp/1/.
var ctx = $('#cv').get(0).getContext('2d');
var img = new Image();
img.src = 'http://www.netstate.com/states/'
+ 'symb/flowers/images/oklahoma_rose.jpg';
img.onload = function() {
ctx.drawImage(img, 0, 0, 300, 300); // Draw image
// Create gradient, from middle to borders
var gradient = ctx.createRadialGradient(150, 150, 0,
150, 150, 150);
// Opaque white in the middle
gradient.addColorStop(0, 'rgba(255,255,255,0)');
// Transparent white at the borders
gradient.addColorStop(1, 'rgba(255,255,255,1)');
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 300, 300); // Fill rectangle over image with the gradient
};
Related
On my project, I originally used CircleStyle to create points on a map and then used fill: new Fill({ with CanvasGradient to style the point with two colours.
I now want to use a custom icon (for example 'icon.png') instead of just a coloured dot for these points.
I have tried using image: new Icon for this which works for displaying the icon but I cannot apply a CanvasGradient to this.
I can apply a single colour to the icon with color which overlays the icon with that colour with transparency, ideally I would want this with 2 colours; half the icon being one colour and half the icon being the other colour.
I have uploaded an image showing how my points currently looked below.
circleStyle point with 2 colours applied using ColourGradient
The documentation suggests that CanvasGradient cannot be applied to icons so my question is: How can I apply two colours/a gradient to an icon in OpenLayers?
The OpenLayers Icon style can take a canvas element as well as an icon url. So you could load the icon url, draw it to a canvas, apply the gradient using a multiply operation, and finally use a destination-in operation to restore transparency:
const img = document.createElement('img');
img.onload = function () {
const width = img.width;
const height = img.height;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const context = canvas.getContext('2d');
const gradient = context.createLinearGradient(0, 0, width, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(0.5, 'red');
gradient.addColorStop(0.5, 'green');
gradient.addColorStop(1, 'green');
context.drawImage(img, 0, 0);
context.globalCompositeOperation = 'multiply';
context.fillStyle = gradient;
context.fillRect(0, 0, width, height);
context.globalCompositeOperation = 'destination-in';
context.drawImage(img, 0, 0);
feature.setStyle(
new Style({
image: new Icon({
img: canvas,
imgSize: [width, height],
})
})
);
};
img.src = icon_url;
Working example https://codesandbox.io/s/icon-color-forked-4b1e1?file=/main.js
I want to change color a Image in canvas
this is the Image
You can see there is a Image transparent I was try using PutImgData but my transparent is changing color
Is there anyway to change color the car and money only ?
I was using this code :
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
image = document.getElementById("testImage");
canvas.height = canvas.width = 100;
ctx.fillStyle = 'red';
ctx.fillRect(10,10,20,10);
ctx.drawImage(image,0,0);
var imgd = ctx.getImageData(0, 0, 45, 45),
pix = imgd.data;
for (var i = 0, n = pix.length; i <n; i += 4) {
if(pix[i+3]==0)
{continue;}
pix.length[i]=r|pix.length[i];
pix.length[i+1]=g|pix.length[i+1];
pix.length[i+2]=b|pix.length[i+2];
pix[i + 3] = 255;
}
ctx.putImageData(imgd, 0, 0);
To mix manually you would have to apply a different formula to mix foreground (new color) and background (image) to preserve anti-aliased pixels (and just in case: the image included in the question is not actually transparent, but I guess you just tried to illustrate transparency using the solid checkerboard background?).
I would suggest a different approach which is CORS safe and much faster (and simpler) -
There are a couple of ways to do this: one is to draw in the color you want, then set composite mode to destination-in and then draw the image, or draw the image, set composite mode to source-in and then draw the color.
Example using the first approach coloring the following image blue:
var img = new Image; img.onload = draw; img.src = "//i.stack.imgur.com/cZ0gC.png";
var ctx = c.getContext("2d");
function draw() {
// draw color
ctx.fillStyle = "#09f";
ctx.fillRect(0, 0, c.width, c.height);
// set composite mode
ctx.globalCompositeOperation = "destination-in";
// draw image
ctx.drawImage(this, 0, 0);
}
<canvas id=c></canvas>
Example using second approach:
var img = new Image; img.onload = draw; img.src = "//i.stack.imgur.com/cZ0gC.png";
var ctx = c.getContext("2d");
function draw() {
// draw image
ctx.drawImage(this, 0, 0);
// set composite mode
ctx.globalCompositeOperation = "source-in";
// draw color
ctx.fillStyle = "#09f";
ctx.fillRect(0, 0, c.width, c.height);
}
<canvas id=c></canvas>
To reset comp. mode back to normal use:
// reset comp. mode
ctx.globalCompositeOperation = "source-over";
As with getImageData(), the drawback with this technique is that your canvas must only hold the content of this image while doing this process. A workaround if the image needs to be colored and mixed with other content is to use an off-screen canvas to do the processing, then draw that canvas back onto the main canvas.
I have an image I am drawing with ctx.drawImage, and it has transparency. I want to darken the image so I am using a fillRect with rgba(0,0,0,0.5) but this darkens the transparent parts of the image too. So I am looking into using ctx.globalCompositeOperation = 'destination atop' including using ctx.save and restore, but this now makes the entire canvas background white and only shows the background through the image/fillrect.
Before ctx.globalCompositeOperation = 'destination atop' or 'source-in':
After:
Here is my code:
/*above is drawing the background, I only want to merge the fillRect with the drawImage*/
ctx.save();
ctx.drawImage(o.image, x, y);
ctx.fillStyle = 'rgba(0,0,0,'+amount+')';
ctx.globalCompositeOperation = 'source-in';
ctx.fillRect(x, y, w, h);
ctx.globalCompositeOperation = 'source-over';
ctx.restore();
Not sure what you are after so just guessing.
Draw transparent image
ctx.globalCompositeOperation = "source-over"; // in case not set
ctx.drawImage(image,0,0);
Use "multiply" to darken image
ctx.globalCompositeOperation = "multiply"
ctx.fillStyle = "rgb(128,128,128)"; // dest pixels will darken by
// (128/255) * dest
ctx.fillRect(0,0,image.width,image.height)
Then to restore the alpha
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(image,0,0);
Then restore default comp state
ctx.globalCompositeOperation = "source-over";
An example of darkening and image. Image on left is darkened via method above. Right image is the original none darkened image. background colour is the colour under the canvas.
const ctx = canvas.getContext("2d");
// create an image to darken
const image = document.createElement("canvas");
image.width = 150;
const ctx1 = image.getContext("2d");
ctx1.beginPath()
ctx1.fillStyle = "#0F0";
ctx1.strokeStyle = "#FA0";
ctx1.lineWidth = 20;
ctx1.arc(75,75,50,0,Math.PI*2);
ctx1.fill();
ctx1.stroke();
ctx.globalCompositeOperation = "source-over"; // in case not set
ctx.drawImage(image,0,10);
ctx.globalCompositeOperation = "multiply"
ctx.fillStyle = "rgb(128,128,128)"; // dest pixels will darken by
// (128/255) * dest
ctx.fillRect(0,10,image.width,image.height)
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(image,0,10);
ctx.globalCompositeOperation = "source-over";
ctx.font = "16px arial";
ctx.fillStyle = "black";
ctx.fillText("Darkened image",10,14);
ctx.drawImage(image,150,10);
ctx.fillText("Original image",160,14);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas>
If you already have pixel content on the canvas you will need to use an offscreen canvas to darken the image and then use that image to draw to the canvas.
// create an image to hold darkened copy of image
const dImage - document.createElement("canvas");
dImage.width = image.width;
dImage.height - image.height;
const dctx = dImage.getContext("2d");
dctx.drawImage(image,0,0);
// apply darkening to dctx as shown in answer above.
ctx.drawImage(dImage,0,0); // draws darkened image on canvas
Is there a way to create an opacity map on a canvas element
I am trying to fade a generated image as shown below.
EXAMPLE:
I don't believe there is any way to directly draw an image with a gradiant mask, but you could pre-draw the image to a separate canvas, and use globalCompositeOperation to draw a masking linear gradient, then draw that canvas using drawImage to the main canvas.
Working Example:
var cvs = document.getElementById('cvs');
var ctx = cvs.getContext('2d');
// Draw some background colors.
ctx.fillStyle = "#FF6666";
ctx.fillRect(0, 0, 150, 200);
ctx.fillStyle = "#6666FF";
ctx.fillRect(150, 0, 150, 200);
// Load the image.
img = new Image();
img.onload = function() {
// Create a canvas in memory and draw the image to it.
var icvs = document.createElement('canvas');
icvs.width = img.width;
icvs.height = img.height;
var ictx = icvs.getContext('2d');
ictx.drawImage(img, 0, 0);
// For masking.
ictx.globalCompositeOperation = 'destination-out';
// Draw the masking gradient.
var gradient = ictx.createLinearGradient(0, 0, 0, icvs.height);
gradient.addColorStop(0, "transparent");
gradient.addColorStop(1, "white");
ictx.fillStyle = gradient;
ictx.fillRect(0, 0, icvs.width, icvs.height);
// Draw the separate canvas to the main canvas.
ctx.drawImage(icvs, 25, 25, 250, 150);
};
img.src = '//i.stack.imgur.com/dR8i9.jpg';
<canvas id="cvs" width="300" height="200"></canvas>
Trying to wrap my head around the HTML5 canvas, I thought I'd create an image carousel, where images would be changed by an opacity gradient sweep, i.e. the same thing as in my fiddle here, only with canvas. I managed to come up with this fiddle, but I can't understand at all what's happening, or rather, why nothing is.
Here's the code:
var outputCanvas = document.getElementById('output'),
ctx = outputCanvas.getContext('2d'),
eWidth = 50,
speed = 5,
cWidth = 480,
img = document.getElementById('newimg'),
x = 0, y = 0,
reqAnimFrame = window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
ctx.drawImage(img, 0, 0, img.width, img.height);
ctx.globalCompositeOperation = "destination-out";
function draw() {
console.log(x);
gradient = ctx.createLinearGradient(x, 0, x+eWidth, 0);
gradient.addColorStop(0, "rgba(255, 255, 255, 0)");
gradient.addColorStop(1, "rgba(255, 255, 255, 1)");
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, img.width, img.height);
}
function animate() {
if (x < 480) {
x += Math.floor((cWidth / 1000) * speed);
console.log(x);
draw();
reqAnimFrame(animate);
}
}
reqAnimFrame(animate);
Calling the draw function by itself it seems to work, but once I start firing it with RequestAnimationFrame it just stops working. The gradient gets drawn once, but even though x is updated in the animation loop, the gradient stays put.
I guess there's something I'm just not getting about how canvas and RequestAnimationFrame work.
Note that I'm not looking for a script or library that does the same thing, but rather I'm hoping to actually understand how canvas works, and in particular why my script doesn't.
Here's one way to do a wipe transition between 2 images using Canvas Compositing:
Original Images (before & after):
Canvas during gradient wipe-transition between the images:
create a transparent-to-opaque gradient that is eWidth pixels wide.
clear the canvas
draw the gradient
fill all pixels to the right of the gradient with opaque
draw the first image with source-in compositing. This will display the first image only where the gradient has non-transparent pixels.
draw the second image with 'destination-over' compositing. This will display the second image "under" the existing first image.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw,ch;
var x=0;
var eWidth=100;
var img1=new Image();
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/sailboat.png";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height;
img1.onload=function(){
requestAnimationFrame(animate);
};
img1.src="https://dl.dropboxusercontent.com/u/139992952/multple/sailboat1.png";
}
function draw() {
// create gradient
gradient = ctx.createLinearGradient(x-eWidth,0, x,0);
gradient.addColorStop(0, "rgba(0,0,0, 0)");
gradient.addColorStop(1, "rgba(0,0,0, 1)");
// save the unaltered canvas context
ctx.save();
// clear the canvas
ctx.clearRect(0,0,cw,ch);
// gradient zone
ctx.fillStyle = gradient;
ctx.fillRect(x-eWidth,0,eWidth,ch);
// fully original right of x
ctx.fillStyle='black';
ctx.fillRect(x,0,cw,ch);
// original image with gradient "dissolve" on left
// set compositing to source-in
ctx.globalCompositeOperation='source-in';
ctx.drawImage(img,0,0);
// revealed image
ctx.globalCompositeOperation='destination-over';
ctx.drawImage(img1,0,0);
// restore the context to its unaltered state
ctx.restore();
}
function animate() {
if (x<cw+eWidth){ requestAnimationFrame(animate); }
x+=5;
draw();
}
$('#again').click(function(){
x=0;
requestAnimationFrame(animate);
});
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Wipe transition between images using canvas</h4>
<button id=again>Again</button><br><br>
<canvas id="canvas" width=300 height=300></canvas>