动画:使图像从一侧到另一侧呈现渐变(不透明度)(所谓的“柔和擦除”效果)

时间:2016-04-11 21:47:12

标签: javascript animation canvas opacity opacitymask

很抱歉,标题令人困惑。我已尽力撰写,如果你想了解我的要求,可以在评论中建议更好的标题。

我想制作的动画可以很容易地通过视频编辑器来完成,但用CSS / JS看起来并不那么容易:首先,我不是在谈论在图像中滑动,图像不是在移动所有。我想让它从一边到另一边出现,但是以渐变的透明方式出现。想象一下,图像上有一个渐变不透明蒙版,使其左端的不透明度为1,右端的不透明度为0.当此蒙版从左侧移动时,它是我想要实现的动画。

我可以将图像分割成几个部分,并且操纵每个部分的不透明度,必须有一定数量的部分才能使整个动画流畅且具有吸引力。

我想的另一种方法是使用画布,你可以按像素操纵图像,如this page所示,我可以做

// get the image data object
var image = ctx.getImageData(0, 0, 500, 200);
// get the image data values 
var imageData = image.data,
length = imageData.length;
// set every fourth value to 50
for(var i=3; i < length; i+=4){  
    imageData[i] = 50;
}
// after the manipulation, reset the data
image.data = imageData;
// and put the imagedata back to the canvas
ctx.putImageData(image, 0, 0);

然而,该页面仅涉及静态图像而非动画。我想知道如果我使用这种方法是否会破坏性能。此外,这种方法涉及许多丑陋的计算。

我认为我想要实现的并不是很奇怪,所以有没有javascript插件来实现这个目标?

3 个答案:

答案 0 :(得分:2)

  

我想制作的动画很容易通过视频编辑来完成,   [...]图像根本没有移动。我希望它从一边到另一边出现

在视频行业,我们称之为软擦(AKA软边抹布)并且它不会太复杂。

您需要的只是一个使用线性渐变可以制作的Alpha蒙版。然后使用带有上下文与xor复合模式相结合的translate属性来为其设置动画。

xor模式的作用是根据绘制到它的alpha通道反转alpha通道。这样做的好处是canvas元素也变得透明,因此任何背景都可以显示出来。您可以保留默认comp。模式也会使背景变黑。

渐变是这样的(颜色值与xor模式无关,只是alpha通道值):

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;

(请参阅 this answer ,了解如何通过创建平滑的渐变来避免&#34;亮线&#34;瑕疵)。

现在创建一个基于位置t绘制完整框架的函数,这是一个与画布宽度相结合的标准化值 - 请记住我们需要使用双倍宽度:渐变+空间用于退出渐变 -

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
}

在动画循环中调用此方法,直至t=2,但可选择将globalCompositeOperation设置为xor,我们就可以了。动画循环本身将为我们重置转换:

演示

&#13;
&#13;
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);
}
&#13;
body {background:url(http://i.imgur.com/OT99vSA.jpg) repeat}
&#13;
<canvas width=658 height=325 id=c></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

使用上下文复合属性通过渐变进行遮罩。创建一个与图像大小相同的屏幕外画布或最小的显示画布大小。

对于每个帧,使用适当的颜色停止(CSS颜色格式rgba(red, green, blue, alpha))创建渐变以设置alpha值。

清除屏幕外的画布

ctxOffScreen.clearRect( 0, 0, ctxOffScreen.canvas.width, ctxOffScreen.canvas.height);

然后将屏幕画布的复合值设置为

ctxOffScreen.globalCompositeOperation = "source-over";

将图像渲染到其上

ctxOffScreen.drawImage(image, 0, 0, ctxOffScreen.canvas.width, ctxOffScreen.canvas.height);

然后将comp设置为

ctxOffScreen.globalCompositeOperation = "destination-in";

这将匹配已经绘制的像素与下一个绘制的相同的alpha值(渐变蒙版)

然后将填充样式设置为您创建的渐变,并在顶部

上绘制一个矩形
ctxOffScreen.fillStyle = gradient;
ctxOffScreen.fillRect( 0, 0, ctxOffScreen.canvas.width, ctxOffScreen.canvas.height);

然后只需将屏幕外画布渲染到屏幕画布

ctx.drawImage(ctxOffScreen.canvas, 0, 0);

如果您使用

ctxOffScreen.globalCompositeOperation = "destination-out";

而不是"destination-in",您将反转使用渐变创建的蒙版。

答案 2 :(得分:0)

作为canvas的替代方案,您可以使用svg。 CSS渐变不是可动画的,但是svg的其他属性是,所以你可以找到一些创造性的方法来为渐变设置动画。

#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>

您可以直接为offset属性设置动画,我还没有测试过。