开发者

Is it possible to make a gradient-transparent/layer masking image using canvas?

开发者 https://www.devze.com 2023-01-08 09:23 出处:网络
I\'ve been following the lessons about transparency and gradients on the Mozilla site: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors but I have not

I've been following the lessons about transparency and gradients on the Mozilla site: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Applying_styles_and_colors but I have not been able to figure this one out.

I know I can achieve these effects with a png image; however, in the program I am working on the gradient will change constantly accordin开发者_如何学运维g to where the image is moved.

Here's an example of the effect I'm looking for. http://home.insightbb.com/~epyonxl1/gradientex.jpg


Its possible to use context.globalCompositeOperation to make the the mask

context.drawImage(img, 0, 0, img.width, img.height, 0,0, img.width, img.height);
context.globalCompositeOperation = "destination-out";
gradient = context.createLinearGradient(0, 0, 0, img.height);
gradient.addColorStop(0, "rgba(255, 255, 255, 0.5)");
gradient.addColorStop(1, "rgba(255, 255, 255, 1.0)");
context.fillStyle = gradient;
context.fillRect(0, 0, img.width, img.height);

This do not do per pixel manipulation and should be faster


To correctly merge two images using a transparency mask it's first necessary to take one of the two images and put it into an off screen canvas, and add the desired transparency mask using context.globalCompositeOperation = destination-out per @Tommyka's answer

var offscreen = document.createElement('canvas'); // detached from DOM
var context = offscreen.getContext('2d');
context.drawImage(image1, 0, 0, image1.width, image1.height);

var gradient = context.createLinearGradient(0, 0, 0, img.height);
gradient.addColorStop(0, "rgba(255, 255, 255, 0.5)");
gradient.addColorStop(1, "rgba(255, 255, 255, 1.0)");
context.globalCompositeOperation = "destination-out";
context.fillStyle = gradient;
context.fillRect(0, 0, image1.width, image1.height);

Then, to actually merge the two images you then need to draw the other image into another canvas, and then simply draw the alpha-composited offscreen canvas on top of that:

var onscreen = document.getElementById('mycanvas');
var context2 = onscreen.getContext('2d');
context2.drawImage(image2, 0, 0, image2.width, image2.height);
context2.drawImage(offscreen, 0, 0, onscreen.width, onscreen.height);

Demo at http://jsfiddle.net/alnitak/rfdjoh31/4/


I've aded some code here: https://code.google.com/archive/p/canvasimagegradient/ that adds a drawImageGradient function to the CanvasRenderingContext2D. You can draw an image with a linear or radial gradient. It doesn't work in IE, even with excanvas, due to the lack of getImageData/putImageData support.

The following code for example will draw an image with a radial gradient (context retrieve and image load not shown):

var radGrad = ctx.createRadialGradient(
    img.width / 2, img.height / 2, 10, 
    img.width / 2, img.height / 2, img.width/2);
radGrad.addColorStop(0, "transparent");
radGrad.addColorStop(1, "#000");

ctx.drawImageGradient(img, 112.5, 130, radGrad);

The code works as follows:

  1. Create a canvas element dynamically and draw the image on it.
  2. Retrieve the imageData for this new canvas.
  3. Retrieve the imageData for the location on the canvas you want to draw the image on to.
  4. Iterate through the destination imageData and update each pixel adding together a percentage (derived from the gradient transparency value) of the image and destination pixel values.
  5. Finally put the updated image data back onto the destination canvas.

Obviously performance is an issue as images get larger. The image on https://code.google.com/archive/p/canvasimagegradient/ it takes about 6-10ms to draw. A 1024x768 image takes about 100ms-250ms to draw. Still usable though as long as you're not animating.


If you need to make an image transparent set the ctx.globalAlpha to whatever you need (1, no transparency, is default). Then reset it after you draw your image. This URL probably will help as well https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Compositing.


I have modernized Alnitak's response by incorporating asynchronous loading of the images.

I also got rid of magic numbers.

const imageUrls = [
  // Base image
  'http://www.netstate.com/states/symb/flowers/images/apple_blossoms.jpg',
  // Gradient image
  'http://www.netstate.com/states/symb/flowers/images/oklahoma_rose.jpg'
];

const main = async () => {
  const ctx = document.getElementById('cv').getContext('2d'),
    baseImg = await loadImage(imageUrls[0]),
    gradImg = await loadImage(imageUrls[1]);

  draw(ctx, baseImg, gradImg);
};

/**
 * Loads an Image object via a Promise.
 * @param {String} url - Location of an image file
 * @return {Promise<Image>} Returns a promise that resolves to an Image.
 */
const loadImage = (url) => new Promise((resolve, reject) => {
  const img = new Image();
  img.addEventListener('load', () => resolve(img));
  img.addEventListener('error', reject);
  img.src = url;
});

/**
 * Draws an image, as a gradient applied to it, above another image.
 * @param {Image} baseImg - Image that is applied under the gradient
 * @param {Image} gradImg - Image to be applied as a gradient
 */
const draw = (ctx, baseImg, gradImg) => {
  const {width, height} = baseImg,
      originX = Math.floor(width / 2),
      originY = Math.floor(height / 2),
      radius = Math.min(originX, originY);

  const offScreen = document.createElement('canvas');
  offScreen.width = offScreen.height = width;
  const ctx2 = offScreen.getContext('2d');

  ctx2.drawImage(gradImg, 0, 0, width, height);
  const gradient = ctx2.createRadialGradient(originX, originY, 0, originX, originY, radius);

  gradient.addColorStop(0, 'rgba(255, 255, 255, 0)');
  gradient.addColorStop(1, 'rgba(255, 255, 255, 1)');

  ctx2.fillStyle = gradient;
  ctx2.globalCompositeOperation = 'destination-out';
  ctx2.fillRect(0, 0, width, height);

  ctx.drawImage(baseImg, 0, 0, width, height);
  ctx.drawImage(offScreen, 0, 0, width, height);
};

main();
<canvas id="cv" width="300" height="300"></canvas>

0

精彩评论

暂无评论...
验证码 换一张
取 消