简单的 canvas 翻角效果

由于工作需求,需要写一个翻角效果:

demo链接:http://jsbin.com/rereqes。

右上角需要从无的状态撕开一个标记,且有动画过程,上图是实现的效果图,不是gif。

对这个翻角效果的难点在于没有翻开的时候露出的是dom下面的内容,实现角度来说纯dom + css动画的设计方案并没有相出一个好的对策: 于是捡起了好久之前学的入门级别的canvas:

下面说一下实现思路。

动画拆分

将此动画分解成两部分,一部分是翻页出现的黑色三角区域,另一个是露出的橘色展示内容

对于橘色的展示内容区域相对好一些,因为是一个规则图形,而黑色区域相对较难

先从基础canvas使用方法说起

布局如上,这里要说一点踩过的坑是,canvas必须要设置上width 与 height,此处并非为css中的width与height,而是写在dom上的属性。因为dom上的width与height标识了canvas的分辨率(个人理解), 所以此canvas画布分辨率为100*100,而展示尺寸是可以通过css控制。

js中首先要做的是获取canvas对象

ctx这个绘画上下文在这个教程中起到的作用至关重要,它提供了非常强大的api,比如用于画线、填充、写文字等,这样看来理解为画笔会更为简明一些。

此处效果需要用到的api如下(不做详细解释,可w3c自行查询):

可能方法列举的不够详尽,见谅。

首先是绘制黑色翻出的部分,图形分解为如下几部分(请根据上图脑补):

左上角向右下的半弧 ╮

然后是竖直向下的竖线 |

然后是向右的半圆 ╰

再然后是向右的横线

接着还是向右下的半弧 ╮

最后是将线连接会起点

于是第一步 我们要先将画笔移动到起始位置:

然后

于是第一个向右下的半弧完成,此时canvas上没有任何绘制内容,因为还没有执行过绘制方法例如stroke或fill。

接下来直线向下就是简单的移动:

这个时候我们接下来应该画向右的半圆,这个时候再用贝塞尔曲线绘制实在有些不太合适,因为从图上来看,这里完全是1/4的圆,所以要使用canvas提供的画圆的api。

上述画圆的代码意为:以(60,40)点为圆心,5为半径,逆时针从180度绘制到90度,180度就是圆心的水平向左 到达点(55,40),与上一步连接上,然后又因为屏幕向下为正,90度在圆心正下方,所以绘制出此半圆。

于是按照相同的步骤,水平向右:

然后再次使用贝塞尔曲线用第一步的思路画出向右下的弧:

同理,上述贝塞尔曲线可以理解为一条从( 75 , 45 ) 到 ( 100 , 50 )的线被 ( 95 , 45 )"吸"成曲线。

最后链接起点,闭合绘画区域:

这个时候黑色区域的翻页就画完了,然后此时开始填充颜色:

我们通过上述代码创建一个从( 50 , 50 )点到(75 , 75)点的线性渐变,颜色从 #ccc 到 #111 到 #000,创建高光效果。

然后填充:

于是翻页效果的一半就算完成了。

至此,我要说一点我领悟的canvas的绘画"套路"。

对于上述教程中,有一步我们使用了一个词叫做闭合,闭合的概念在canvas中是真是存在的,对于fill方法来说,填充的区间是有一个空间尺寸才可以的,比如我们绘画的这个黑色的三角形,加入我们最后没有将终点与起点相连接,同样canvas会自动帮我们链接最后一笔绘画的位置到起点,强制行程闭合空间,而这样我们想再多画几个新的闭合空间就麻烦了,所以canvas提供了如下api 新建闭合路径:

所以对于我们接下来要绘制右上角橘色区域来说,我们在绘制黑色区域之前首先要做的是:

然后在fill之前我们应该 :

也就是说beginPath到closePath之间标识着我们自己的一个完整的绘画阶段。

那么接下来绘制右上角的橘色区域就简单很多了:

于是右上角的橘色区域我们就绘制完成了。

文字绘制

接下来绘制"new",实际上是使用canvas简单的文本绘制,代码如下:

对于上述代码中,文字的相关api是属于没有难度的,只是设置而已,需要理解的部分在于translate和rotate。

这两个方法中,translate的意思为移动canvas画布的( 0 , 0 )点到 (78,22),然后旋转45度,再将文字渲染在原点,实际就是(78,22)这个点上,此时我们对canvas的画笔做出了非常大的修改。

比如我们修改了旋转角度以及画布圆点,这种操作或许只在我们需要绘制倾斜的new的时候需要,后期可能就不需要使用了。

还好canvas的画笔是存在"状态"的,通过可以保存当前画笔的状态,通过ctx.restore();可以恢复到上次画笔保存的状态。

于是我个人理解到,在开发canvas动画时,一个较好的习惯就是,在beginPath之前先ctx.save();保存画笔状态,在closePath后恢复之前的画笔状态,这样我们的每一个绘制阶段对于画笔的修改都将是不会有影响的(个人经验)。

代码中这部分是指 我们绘制的文字new与橘色三角形区域的重叠关系,此方法取值较多,此处不做过多介绍,source-atop值可以使重叠区域保留,新绘制的内容在重叠区域以外的部分消失,以此达到new在里面的效果。

到这里我们就开发好了翻角效果的完全展示的状态,那么如何让这个区域动起来呢?

此处需要使用h5提供的用于刷帧的函数。

此方法可简单理解为16毫秒的定时器,但是厉害的是可以再各个环境中自动匹配到可达到的相对顺畅的帧率,实际并不是定时器哈。

我们需要在这个循环执行的函数中,将上述的绘制内容重复绘制,例如 :

这样我们就可以达到刷帧的效果了,于是接着我们要做的就是控制绘制时各个数值的参数。

比如我们是以(50,0)为起点,(100,50)为终点这样的两个移动点为绘制标记的,如果我们将两个点进行存储,并且每次执行drawMethod的时候更新点的位置,然后清空canvas,再绘制新的点 那么就可以达到canvas动起来的目的了。

实际效果链接在这里:http://jsbin.com/rereqes。

在上面的demo链接中,自己定义了一个速度与加速度的关系,比如每次绘制一次canvas后,将存储的点坐标进行增加一个speed值,然后speed值也增加,这样speed对应的概念就是速度,而speed的增加值对应的就是加速度。 所以就呈现了一种加速运动的状态。

以上内容纯属个人理解内容,若果有哪里理解错了欢迎各位大大指点。

作者: 敖爽

原文:https://segmentfault.com/a/1190000012101315

本文来自企鹅号 - 前端教程媒体

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏cnblogs

Css3新特性总结之边框与背景(二)

一、条纹背景 利用background为linear-gradient函数实现,linear-gradient取值如下: <angle>:角度,渐变的方向 t...

1809
来自专栏腾讯AlloyTeam的专栏

教你用 webgl 快速创建一个小世界

Webgl的魅力在于可以创造一个自己的3D世界,但相比较canvas2D来说,除了物体的移动旋转变换完全依赖矩阵增加了复杂度,就连生成一个物体都变得很复杂……这...

1.6K0
来自专栏Coco的专栏

妙用 scale 与 transfrom-origin,精准控制动画方向

1524
来自专栏Coco的专栏

谈谈一些有趣的CSS题目(十四)-- 纯 CSS 方式实现 CSS 动画的暂停与播放!

1363
来自专栏Python中文社区

Python用Pillow(PIL)进行简单的图像操作

專 欄 ❈ sunhaiyu,Python中文社区专栏作者 专栏地址: http://www.jianshu.com/u/4943cb2c6ea4 ❈ Pyt...

29810
来自专栏为数不多的Android技巧

Xfermode in android

Xfermode有三个实现类:AvoidXfermode, PixelXorXfermode以及PorterDuffXfermode。 前两个类因为不支持硬件加...

984
来自专栏吉浦迅科技

DAY16:阅读纹理内存之纹理对象API

815
来自专栏我杨某人的青春满是悔恨

仿【每天】首页动画

最近工作一直挺忙,偶有闲暇时都在翻看《iOS Animations by Tutorials》,受益良多,尤其是让我对Core Animation有了更深入的理...

692
来自专栏落影的专栏

GPUImage详细解析(二)

解析 GPUImage详细解析(一) 上一篇介绍的是GPUImageFramebuffer和GPUImageFilter。 简单回顾一下: GPUImag...

2813
来自专栏Coco的专栏

谈谈一些有趣的CSS题目(九)-- 巧妙的实现 CSS 斜线

1453

扫码关注云+社区