HTML5 画布中的计算阴影

在视觉效果方面,HTML5 最棒的新功能之一是画布元素及其 API。表面上,它看起来并不起眼 - 页面上只是一个可以绘制和擦除的矩形。就像一个画板。然而,一旦掌握了它的坐标系的变换、旋转和缩放能力,就会发现它确实非常强大。

今天我想快速展示如何用它来做(模拟)一些比较复杂的事情,比如元素上的计算阴影。要了解我的意思,请查看以下演示,该演示也位于演示工作室


查看 JSFiddle 上的动画版本

(这是使用 JSFiddle 来向您展示演示,因此您可以单击不同的选项卡来查看 JavaScript 和 CSS 需要的效果。所有演示也都在 GitHub 上可用。)

如您所见,阴影变得更加模糊,并且随着“太阳”距离它越远,其突出程度越低。您可以使用鼠标在以下演示中查看效果


查看 JSFiddle 上的鼠标启用演示

让我们来看看它是如何完成的。第一步是拥有一个可以绘制的画布 - 只需使用一个鼠标检测脚本(我们使用了多年)和一个可以访问其 API 的画布即可


查看 JSFiddle 上的步骤一

单击上面示例的播放按钮,您会看到您可以在画布上绘制。但是,问题是您一直在画布上绘制,而不是让球体只跟随光标。为此,我们需要在每次鼠标移动时擦除画布。您可以使用clearRect()来实现这一点


查看 JSFiddle 上的步骤二

现在运行上面的示例会显示球体随鼠标移动。很酷,所以这将是我们的“太阳”。现在我们需要在画布上放置一个物体来投射阴影。我们可以在某处简单地绘制它,但我们真正想要的是它位于画布的中间,而阴影从该点向左和向右移动。您可以使用translate()移动画布坐标系的原点。这意味着我们的球体现在与鼠标偏移


查看 JSFiddle 上的步骤三

如果您选中“修正鼠标位置”复选框,您会看到这一点已得到修正。当我们将坐标系移动到画布宽度和高度的一半时,我们还需要从鼠标 x 和 y 位置中减去这些值。

现在我们可以使用c.moveTo( 0, 0 );c.lineTo( distx, disty );绘制一条从画布中心到鼠标位置的线,以查看距离,其中distxdisty是移动后的鼠标位置值


查看 JSFiddle 上的步骤四

为了找出阴影的距离,我们只需要将鼠标坐标乘以 -1 - 在此演示中显示为红线


查看 JSFiddle 上的步骤五

这给了我们从中心到鼠标位置相反方向的阴影距离,但我们不想要全部长度。因此,我们可以对长度应用一个因子,在本例中为 0.6 或 60%


查看 JSFiddle 上的步骤六

现在我们准备好了进行一些阴影操作。您可以使用shadowColor对画布对象应用阴影,其距离是shadowOffsetXshadowOffsetY。在本例中,这是红线的末端,即从鼠标位置到画布中心的反向因子距离


查看 JSFiddle 上的步骤七

现在,让我们模糊阴影。模糊是使用shadowBlur属性完成的,它是一个从 0 到模糊强度的数字。现在我们需要找到一种方法来从鼠标到画布中心的距离计算模糊强度。幸运的是,毕达哥拉斯 多年前就为我们找到了如何做到这一点的方法。由于鼠标的 x 和 y 坐标是直角三角形的直角边,我们可以计算斜边长度(点到画布中心的距离)使用坐标的平方和的平方根,或Math.sqrt( ( distx * distx ) + ( disty * disty ) )

这给了我们像素距离,但我们真正想要的是一个更小的数字。因此,我们再次可以计算模糊强度的因子 - 在这里,我们使用一个数组来表示最弱和最强的模糊blur = [ 2, 9 ]。由于画布本身也具有从中心到顶部边缘点的直角三角形,我们可以使用longest = Math.sqrt( ( hw * hw ) + ( hh * hh ) )计算最长距离,其中hw是画布宽度的一半,hh是高度的一半。现在我们要做的就是计算用于乘以距离的因子,即blurfactor = blur[1] / longest。绘制画布时产生的模糊是鼠标位置的距离乘以因子,即currentblur = parseInt( blurfactor * realdistance, 10 );。我们忽略低于我们之前定义的范围的模糊值,这样就得到了模糊阴影


查看 JSFiddle 上的步骤八

为了使阴影随着鼠标距离越远而变弱,我们可以使用其rgba()颜色的 alpha 值。与模糊相同的原理适用,我们将边缘值设置为shadowalpha = [ 3, 8 ],在根据距离计算它们之后,我们将它们的逆值作为 alpha 值应用,即c.shadowColor = 'rgba(0,0,0,' + (1 - currentalpha / 10) + ')';。这会使阴影变得模糊和变弱


查看 JSFiddle 上的步骤九

您可以使用它做更多事情,例如,我们还可以缩放太阳球体,使其随着距离越远而变大,或者使用第二个形状来调整其大小和模糊程度。你也可以完全过分

找到了优化它的方法?告诉我们吧!

关于 Chris Heilmann

HTML5 和开放网络的布道者。让我们一起解决这个问题!

Chris Heilmann 的更多文章…


5 条评论

  1. 基本

    您的意思是几个世纪前 :)

    2011 年 8 月 29 日 下午 6:23

  2. ybochatay

    画布很棒。但我无法理解它为何如此受欢迎,以至于取代了 SVG,而 SVG 已经可以做到这一点很多年了。

    2011 年 8 月 29 日 下午 9:55

    1. Chris Heilmann

      我想它的主要优点是可以内嵌在 HTML 中,并且所有支持它的浏览器都可以使用它。SVG 的内嵌支持要比 SVG 本身晚得多。

      2011 年 8 月 30 日 上午 4:43

  3. 样式事物

    而且画布在几乎所有情况下都比 SVG 快得多(不包括 IE9+,在 IE9+ 中 SVG 也很快)。

    很棒的演示!非常棒的工作

    2011 年 8 月 30 日 下午 5:06

  4. ybochatay

    我很快用 SVG 重写了这个示例,以查看区别
    http://jsfiddle.net/ybochatay/D3Ygk/1/
    我觉得在这种情况下,操作 DOM 对象比在每一帧都重新绘制所有内容更简单。但我想当您需要动画化很多东西时,画布会更好。

    2011 年 8 月 31 日 上午 7:19

本文的评论已关闭。