Animation: Making An Image Gradiently Appear From Side To Side (in Opacity)(so Called "soft-wipe" Effect)
Solution 1:
The animation I am trying to make can easily be done by video editors, [...] the image is not moving at all. I want it to appear from side to side
In the video industry we call this a soft-wipe (AKA soft edged wipe) and it's not too complicated to make.
All you need is an alpha mask that you can make using a linear gradient. Then use translate properties with the context combined with xor composite mode to animate it.
What xor mode does is to invert the alpha channel based on the alpha channel drawn to it. The advantage of this is that the canvas element get transparent as well, so any background can show through. You can keep the default comp. mode as well which will make the background black instead.
The gradient is made like this (the color values does not matter with xor mode, just the alpha channel values):
var g = ctx.createLinearGradient(0, 0, ctx.canvas.width, 0);
g.addColorStop(0, "rgba(0,0,0,0)");
g.addColorStop(1, "rgba(0,0,0,1)");
ctx.fillStyle = g;
(see this answer for how to avoid "bright line" artifacts by creating a smoothed gradient).
Now create a function that draws a complete frame based on position t
which is a normalized value combined with canvas width - have in mind we need the double width to work with: gradient + room for the gradient to exit -
function render(t) {
var w = t * ctx.canvas.width; // width based on t
ctx.drawImage(img, 0, 0); // render bg. image
ctx.translate(-ctx.canvas.width + w, 0); // translate on x-axis
ctx.fillRect(0, 0, ctx.canvas.width * 2, ctx.canvas.height); // render gradient mask
}
Call this in an animation loop until t=2
, but optionally set globalCompositeOperation
to xor
and we're good to go. The animation loop itself will reset transformation for us:
Demo
var ctx = c.getContext("2d"),
img = new Image,
t = 0, step = 0.02
// alpha mask
var g = ctx.createLinearGradient(0, 0, ctx.canvas.width, 0);
g.addColorStop(0, "rgba(0,0,0,0)");
g.addColorStop(1, "rgba(0,0,0,1)");
ctx.fillStyle = g;
ctx.globalCompositeOperation = "xor";
// load bg image
img.onload = animate;
img.src = "http://i.imgur.com/d0tZU7n.png";
function animate() {
ctx.setTransform(1,0,0,1,0,0); // reset any transformations
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
render(t);
t += step;
if (t <= 2) requestAnimationFrame(animate); // 2 since we need double width
else {t=0; setTimeout(animate, 2000)}; // just to repeat anim. for demo
}
function render(t) {
var w = t * ctx.canvas.width; // width based on t
ctx.drawImage(img, 0, 0);
ctx.translate(-ctx.canvas.width + w, 0); // translate on x-axis
ctx.fillRect(0, 0, ctx.canvas.width*2, ctx.canvas.height);
}
body {background:url(http://i.imgur.com/OT99vSA.jpg) repeat}
<canvas width=658 height=325 id=c></canvas>
Solution 2:
Use the context composite property to do the masking via a gradient. Create an offscreen canvas the same size as the image or the size of the display canvas whichever is smallest.
For every frame create the gradient with the appropriate colour stops (CSS color format rgba(red, green, blue, alpha)
) to set the alpha values.
Clear the off screen canvas
ctxOffScreen.clearRect( 0, 0, ctxOffScreen.canvas.width, ctxOffScreen.canvas.height);
Then set the composite value for the of screen canvas to
ctxOffScreen.globalCompositeOperation = "source-over";
Render the image onto it
ctxOffScreen.drawImage(image, 0, 0, ctxOffScreen.canvas.width, ctxOffScreen.canvas.height);
Then set the comp to
ctxOffScreen.globalCompositeOperation = "destination-in";
This will match the pixels already drawn with the same alpha value that is in what every you draw next (the gradient mask)
Then set the fill style to the gradient you created and draw a rectangle over the top
ctxOffScreen.fillStyle = gradient;
ctxOffScreen.fillRect( 0, 0, ctxOffScreen.canvas.width, ctxOffScreen.canvas.height);
Then just render the offscreen canvas to the onscreen canvas
ctx.drawImage(ctxOffScreen.canvas, 0, 0);
If you use
ctxOffScreen.globalCompositeOperation = "destination-out";
instead of "destination-in"
you will invert the mask you created with the gradient.
Solution 3:
As an alternative to canvas
, you can use svg
. CSS gradients are not animateable, however other properties of svg
are, so you can find some creative ways to animate gradients after all.
#Mask rect {
x: 400px;
transition: 1s;
}
svg:hover #Mask rect {
x: -400px;
}
svg {
border: 2px solid black;
background-color: #ee3377;
}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400">
<defs>
<linearGradient id="Gradient">
<stop offset="0" stop-color="white" stop-opacity="0" />
<stop offset=".5" stop-color="white" stop-opacity="1" />
</linearGradient>
<mask id="Mask">
<rect width="800" height="400" fill="url(#Gradient)" />
</mask>
</defs>
<image xlink:href="http://i.imgur.com/g3D5jNz.jpg" width="400" height="400" mask="url(#Mask)"></image>
</svg>
You could probably animate the offset
property directly, I haven't tested yet.
Post a Comment for "Animation: Making An Image Gradiently Appear From Side To Side (in Opacity)(so Called "soft-wipe" Effect)"