笔者是个小萌新,最近刚刚学习了css、canvas、svg这几种常用的画图方法,所以打算使用这3钟方法制作一个小logo,以加深自己对这方面知识的掌握。废话不多说,先放上本次要制作的logo~

由上面的腾讯logo图中,我们可以看到logo的组成大致为周围环绕的绿、黄、红三色条形图案,以及中间的小企鹅。这个logo的元素并不复杂,所以一开始我选择从较简单的3色条形图案入手。
通过观察logo外围的图形,可以大概得到一个思路
①、3个不同颜色的图案的形状是完全一样的,我们可以通过画出其中一个,定好旋转 的中心后,得到另外的两个
②、图形具有一定的弧度,所以我首先想到的是通过贝塞尔函数的方法去绘制,但是 因为取得参数需要花费较多的时间,所以打算采用绘制圆弧的方法去绘制。
③、该图案至少由3个不同半径的圆组成闭合的图形,所以首先我们需要得到构成图形 的几个圆的圆心的相对位置及半径。
④、通过上网查找和计算绘制这些圆弧所使用的一些参数,通过下面的视频链接可以 得到一些绘制的数据www.bilibili.com/video/av381…。(链接内容仅供参考,本文参数仅供参考,可以通过自己的方法取得参数)
有了上面大概的思路之后笔者就开始了图形的绘制(下面例子统一以右上方的绿色图形为样本开始绘制)
1、Canvas绘制图形
首先,准备我们的画板,创建一个canvas元素。
<body>
<canvas id="canvas" width ="600" height ="600">你的浏览器不支持canvas</canvas>
<script type="text/javascript" >
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
</script>
</body>
通过beginPath()方法创建一个新的绘制路径,并且设置canvas的坐标原点,绘制线条的宽度和颜色。
ctx.beginPath();
ctx.rotate(Math.PI*240/180);
ctx.strokeStyle = "green" ;
ctx.lineWidth = 3;
使用arc()方法绘制圆弧,arc()方法的几个参数按顺序分别为圆心的x、y坐标,半径r,起始的角度、结束的角度,而最后的参数true为逆时针绘制,false为顺时针绘制,默认为顺时针(false);
ctx.arc(29,-116,30,Math.PI*1.1/2,Math.PI*3.08/2);
ctx.arc(1,1,150,Math.PI*3.13/2,Math.PI/9);
ctx.arc(42,50,100,0,Math.PI*3.54/2,true);
ctx.arc(-13,59,150,-Math.PI/5.99,Math.PI*3.14/2,true);
在绘制的时候有几个需要注意的地方。这几个圆弧是在同一条路径里面的,这样在最后形成闭合路径的时候,才能通过fill()方法对绘制出的图形上色。arc()方法绘制的圆形起始位置需要注意,这里就不用图片标示了。还有里面的角度设置,无论是顺时针画还是逆时针画,角度的大小都是从起始位置按顺时针计算的,也可以这么理解,我们平时看的x,y轴的正轴是往→和↑的,而这里的坐标轴的正轴是往→和↓的,因此造成了角度的区别。
最后通过闭合路径,绘制并上色之后得到了我们需要的图形
ctx.closePath();
ctx.stroke()

ctx.fillStyle = "green";
ctx.fill();

接着,我们新创建一条路径,旋转一定的角度之后,开始和之前一致的绘制方法就可以得到另外两个图形了。
ctx.beginPath();
ctx.rotate(Math.PI*240/180);
!!注意:这里画布的旋转一定要添加到绘制图形之前,rotate()方法不对绘制之前的图形生效。第三个绘制的图形要在上一次画布旋转的基础上进行旋转,而不是按照120°、240°就可得到最终图形。
由于企鹅的绘制需要使用贝塞尔函数实现,所以在这里我通过canvas导入一张企鹅的图片完成logo。这里需要注意的是,由于图片的加载是需要一定的时间的,所以需要通过onload事件来判断进行图片的绘制。不然,会出现使用了drawImage()方法却没看到图片的情况。
ctx.beginPath();
var img = new Image();
img.src = "images/qie.png";
img.onload = function(){
ctx.rotate(Math.PI*240/180);
ctx.drawImage(img,-60,-60,120,120);
}
附上绘制结果图

2、svg绘制图形
由于这次制作的图形是不规则的,所以我想到的是通过使用svg的path标签构建一个路径。并设置好线条和颜色。
<body>
<svg width="600" height="600" id="logo">
<path class="shape" stroke = "green" stroke-width ="2" fill="green"></path>
</svg>
</body>
首先照例取得path,并且设置绘制路径的坐标圆点。
<script type="text/javascript">
var path = document.getElementsByClassName("shape");
//以坐标(300,300)为圆心
path[0].setAttribute('transform','translate(300,300)');
</script>
定义一些基本的参数。
//每段圆弧对应的圆的半径
var r1 = 30;
var r2 = 150;
var r3 = 100;
var r4 = 150;
//起始位置
var cx = 90*Math.cos(Math.PI*75/180);
var cy = -90*Math.sin(Math.PI*75/180);
//每一段圆弧的结束位置
var x1 = 150*Math.cos(Math.PI*75/180);
var y1 = -150*Math.sin(Math.PI*75/180);
var x2 = 150*Math.cos(Math.PI*20/180);
var y2 = 150*Math.sin(Math.PI*20/180);
var x3 = 125;
var y3 = 0;
这里来说明一下path的d属性的设置。这里由于我们需要绘制圆弧,可以知道,我们需要使用的是起始位置M和绘制圆弧路径A这两个参数。其中d=M 0 0表示画笔的初始位置,后接A rx ry Xrotation flag1 flag2 x y , 这几个参数分别为
横轴(rx)和纵轴(ry)的大小(绘制圆的话两者一致),
X为相对于坐标的旋转角度,
flag1是 绘制大弧或小弧
flag2 是 (1)顺时针绘制,(0)逆时针绘制
x、y为绘制的终点坐标
由上面可以发现,同样是通过闭合路径的绘制,svg和canvas是有一定的差别的。canvas绘制圆弧是通过 圆心的坐标x、y,半径r,起始和结束位置的角度来确定圆弧的路径的,而svg是通过起始和结束位置的坐标,根据半径r来得到圆弧,并且由于两点之间是确定不了具体的圆弧的,所以还需要加上flag1、flag2来得到唯一的圆弧。所以,我使用svg绘制圆弧的时候,不必知道圆心的位置和旋转的角度,但是要计算出每段圆弧的始末位置,这也是上面定义的内容。
var dOne = ['M',cx,cy,'A',r1,r1,0,1,1,x1,y1];
var dTwo = ['A',r2,r2,0,0,1,x2,y2];
var dThree = ['A',r3,r3,0,0,0,x3,y3];
var dFour = ['A',r4,r4,0,0,0,cx,cy];
var d = dOne.concat(dTwo,dThree,dFour);
path[0].setAttribute('d',d.join(" "));
通过把每一段圆弧的各项参数放入到一个数组中,并且最终连接起来就是一条完整的路径了。这里有一点需要注意,M参数定义的起始位置在一条闭合路径中只需要有一个就行,在之后的圆弧中添加M的相当与建立新的路径,这样在为整个图形上色的时候就会出现错误。
最后,完成一个图形后,添加另外两个图形的path标签,并且在path中transform属性中添加不同的旋转角度,就可以得到另外两个图形了。(企鹅通过css添加图片如何添加的代码在css绘制中会说明)
<svg width="600" height="600" id="logo">
<path class="shape" stroke = "green" stroke-width ="2" fill="green"></path>
<path class="shape" stroke = "yellow" stroke-width ="2" fill="yellow"></path>
<path class="shape" stroke = "red" stroke-width ="2" fill="red"></path>
</svg>
path[0].setAttribute('transform','translate(300,300)');
path[1].setAttribute('transform','translate(300,300)rotate(240)');
path[2].setAttribute('transform','translate(300,300)rotate(120)');
在旋转方面,canvas和svg也是不太一样的。canvas旋转的是整块画布,所以后面绘制的路径统一受到影响,而svg是单独对路径进行旋转,路径互相之间不会影响。
path[0].setAttribute('d',d.join(" "));
path[1].setAttribute('d',d.join(" "));
path[2].setAttribute('d',d.join(" "));
最后的结果图

3、css绘制图形
在css制作中,我的想法是把图形拆分成两个图形,一个半圆作为头部,一个是尾巴作为身体部分,两段拼接就是要制作的图形。 下面是这两部分的制作方法。
.lHead{
width:30px;
height:60px;
background: green;
border-radius:30px 0 0 30px ;
position:absolute;
}

.lBody{
width:150px;
height:90px;
position:absolute;
border-top:60px solid green;
border-radius:0 150px 0 0 ;
left:29px;
}


制作完头部和尾巴两个图形,只需要通过定位连接起来,就完成其中的一个图形了。
body{
margin:50px;
margin-left:300px;
position:relative;
}
创建3个div来转载头部和尾巴组合的图形。(结构如下图)
<body>
<div class="logo rotateG">
<div class="lHead green"></div>
<div class="lBody greenB"></div>
</div>
<div class="logo rotateY">
<div class="lHead yellow"></div>
<div class="lBody yellowB"></div>
</div>
<div class="logo rotateR">
<div class="lHead red"></div>
<div class="lBody redB"></div>
</div>
<div id="qie"></div>
</body>
为3个图形设定一样的旋转中心。
.logo{
position:absolute;
transform-origin:30px 150px;
}
每个图形旋转不同的角度。
.rotateR{
transform: rotate(140deg);
}
.rotateY{
transform: rotate(260deg);
}
建立不同的背景颜色样式,和边框颜色样式(因为尾巴部分需要的是边框颜色的样式)
.green{
background: green;
}
.greenB{
border-color:green;
}
.red{
background: red;
}
.redB{
border-color:red;
}
.yellow{
background: yellow;
}
.yellowB{
border-color:yellow;
}
导入企鹅的图片。
#qie{
background: url("images/qie.png")no-repeat;
background-size: cover;
width:120px;
height:120px;
position: absolute;
top:90px;
left:-30px;
}
最终得到的结果。

Css制作题外话:一开始用css绘制的时候,我是想通过不同的圆形遮挡的方式绘制出图形,图中为蓝色、绿色两个大圆,红色、黄色两个小圆,以及灰色的遮挡块,把相应的图形设置为白色后。


可是当旋转的时候发现,那些用来遮挡的部分,同样遮挡住了两外两个绘制的条状图形,导致无法同时显现完整的图案,因此最后才使用了上面的拼接图形的方法。
关于css、canvas、svg制作的一些心得体会。
1、从最直观的成品来说,三个最终图形的效果是差不多的,但是如果放大之后,canvas会出现模糊的现象,svg、css的图案依旧清晰。这是因为canvas的图形是位图,是由像素点组成的,而svg、css的图案是矢量图,主要是线条和颜色块构成,因此放大图片之后,canvas的图像会出现模糊。
2、从代码的数量来看,如果只是绘制出一个图形则canvas代码量最少(由于本次绘制中svg中包含了起始结束点的计算,代码量较大),而绘制完整的图形时,三者差不多。
3、从绘制图形难度来说,css是3者中最简单的,只需要知道图形长宽的大小,并且应用相应的样式即可。而canvas、svg之间各有优势。在上面svg绘制的过程中,我有说明过两者的绘制条件上的一些区别。就圆弧来说,如果初始、结束位置比较容易得到,svg无疑会简单很多,但是像上面我已经知道需要多少角度的圆弧,还要通过半径r计算起始结束位置就会很麻烦,而这时候canvas较好用。(ps:得到合适的圆弧参数真的花了不少时间。)
4、从图像的路径连续程度的方面来说,css拼接图形的时候难免会有1、2像素的误差,有可能出现拼接口不连续,或者图形之间不够紧密的情况。而canvas和svg都是路径构成的,不会出现不连续的情况。但是我在使用canvas绘制圆弧的时候,两段圆弧之间也不是完美接合上的,只是因为同一路径的原因首尾连接上了而已。而svg则不同,每一段的结尾是下一段的开始,不会有不连续的问题存在,这也是起始、结束位置的优点。
5、从圆弧的修正容易程度来说,canvas是较容易的,通过修改圆心,半径,角度,都可以做到微调整。而css比较困难做到对图形的细微调整,只能通过边框和长宽改变图形的总体外形,或者拼接更多的图形来达到复杂的图像。
6、由于篇幅关系,笔者就没有在文章中写下自己构思可以添加的一些交互动画效果了,可是关于这3种方法在交互操作时的一些区别还是需要说一下。在实现交互方面,canvas相对于另外两者就不占什么优势了。canvas不能直接通过内部图形绑定事件,需要通过获取鼠标的位置绑定。而css、svg绘制的图像都有元素标签进行绑定事件,所以可以在交互较多的情况下使用。
以上就是笔者本次一个小小制作的思路,制作过程,以及自己的一些感想。