Apply a Gradient to an Icon in Open Layers - javascript

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

Related

How to Style Images in a canvas?

I'm using node-canvas and I was wonder how style an imported image in canvas similar to how you would an image in CSS.
For example, how would I crop a square image in canvas to a circle. In CSS, all you need to do is set border radius to 50%.
Well obviously you cannot use CSS in this case since CSS is applied to the DOM and not the the pixel based content of a Canvas element.
However the Canvas element has its own set of draw functions which allow to you replicate or at least approximate CSS rules.
Since you mentioned cropping an image to a circle I'll focus on this example. To achieve this effect you want to specify a clipping region before drawing the image. Every pixel outside of the clipped region will not be drawn. Effectively this will crop the image to the clipped region.
In code:
// Retrieve canvas and get context
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
// Save the context so we can undo the clipping region at a later time
context.save();
// Define the clipping region as an 360 degrees arc at point x and y
context.beginPath();
context.arc(x, y, radius, 0, 2 * Math.PI, false);
// Clip!
context.clip();
// Draw the image at imageX, imageY.
context.drawImage(image, imageX, imageY);
// Restore context to undo the clipping
context.restore();
I'd advice taking a look at this page to give you an idea of what you can do with the Canvas element and the 2D rendering context.
I don't know if this would work in node, However you can do this with canvas;
The simplest way of doing it is using, as you intended, border-radius:
canvas{border-radius:50%;}
An other way of doing it is by using the ctx.clip() method.
let canvas = document.querySelector("canvas");
let ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.arc(125,120,100,0,2*Math.PI);
// you clip the context
ctx.clip();
let img = document.querySelector("#testImg");
ctx.drawImage(img, 0, 20);
<canvas width="250" height="240" >
<img id="testImg" src="theImage.jpg">
</canvas>
Yet an other way of doing it is by using ctx.globalCompositeOperation = "destination-atop"in this way:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 400,
cx = cw / 2;
let ch = canvas.height = 400,
cy = ch / 2;
ctx.globalCompositeOperation = "destination-atop";
let img = document.querySelector("#testImg");
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.fillStyle = "#f00";
ctx.arc(cx, cx, 100, 0, 2 * Math.PI);
ctx.fill();

Change color Image in Canvas

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.

Gradient map as opacity in a HTML5 canvas element?

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>

Revealing portions of an image on mouseover

What is the best way to go about having a "fog of war" type thing where an image is blacked out, but then as the user mouses over the image an area around the cursor is revealed. So probably some layer styled with CSS over an image, that as the user mouses over it becomes transparent and so the image can be seen in an area around the mouse, but the rest of the image is still blacked out.
If you just want to use javascript and css to do this:
Create a black image with a transparent hole in the middle
Use some javascript to get the mouse position
Update the css to set the position of the fog image to the mouse pointer
You would have to make sure everything is layered correctly so that your image is under the fog image, and the fog image is under the rest of the content if this does not take up the entire browser window.
I found this to be a very nice question, so for future visitors I will leave this as a reference:
$(window).on('load', function () {
var
ctx = $('#canvas')[0].getContext('2d'),
mouse = {x: -100, y: -100};
// fill black
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// track mouse
$('#canvas').on('mousemove.fog', function (evt) {
mouse.x = evt.offsetX;
mouse.y = evt.offsetY;
});
(function animloop(now) {
var
frame = webkitRequestAnimationFrame(animloop), // use a polyfill here
x = mouse.x,
y = mouse.y,
r = 10,
grad = ctx.createRadialGradient(x, y, 0, x, y, r);
grad.addColorStop(0, "rgba(0, 0, 0, 255)");
grad.addColorStop(1, "rgba(0, 0, 0, 0)");
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = grad;
ctx.arc(x, y, r, 0, 2 * Math.PI, true);
ctx.fill();
}(Date.now()));
});​
demo: http://jsfiddle.net/RUc5s/1/
On canvas, you could make a layer over the image that is partly transparent but the area near the cursor will be fully transparent. Layers don't really exist on canvas, but there are frameworks that allow you to do layers.
on HTML/CSS, you could do "tiles" of the image that have 2 layers, an image below and a partly transparent PNG above. On hover, the PNG of the tile is set to display:none to reveal the image underneath.

Image gradient on HTML5 canvas

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
};

Categories