一个小小的实例,几乎包含了Canvas的所有用法。
关于可重用代码,有一条重要的建议:一般绘制都应从原点开始,应用变换(缩放、平移、旋转等),然后不断修改代码直至达到希望的效果。
比如我们要画一片小树林,那么我们可以封装一个在原点画一棵树的方法,然后通过变换,重复调用这个画树的方法,即可实现画出一片树林的效果。
效果图:
用到的小路背景图:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas变换</title>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script type="text/javascript">
var canvas = document.getElementById('myCanvas');
canvas.width = 500;
canvas.height=500;
var ctx = canvas.getContext('2d');
//绘制树冠 只画路径 不填充 因为画阴影的时候还需要复用这段代码
function treeCrown(context){
// 绘制树冠
context.beginPath();
context.moveTo(-25,-50);
context.lineTo(-10,-80);
context.lineTo(-20,-80);
context.lineTo(-5,-110);
context.lineTo(-15,-110);
// 树顶
context.lineTo(0,-140);
context.lineTo(15,-110);
context.lineTo(5,-110);
context.lineTo(20,-80);
context.lineTo(10,-80);
context.lineTo(25,-50);
context.closePath();
}
// 绘制完树冠后的描边,填充
function treeCrownFill(context){
context.lineWidth = 4;
context.lineJoin = 'round';
context.strokeStyle='#663300';
context.stroke();
context.fillStyle='#339900';
context.fill();
}
// 绘制树干
function treeTrunk(context){
// 树干 普通填充
/*
context.beginPath();
context.fillStyle='#663300';
context.fillRect(-5,-50,10,50);// x y width height
*/
// 树干 使用渐变填充
let trunkGradient = context.createLinearGradient(-5,-50,5,-50);//水平渐变
trunkGradient.addColorStop(0,'#663300');//一般棕色
trunkGradient.addColorStop(0.4,'#996600');//中间颜色淡一些
trunkGradient.addColorStop(1,'#552200');// 右侧颜色深一些
context.fillStyle=trunkGradient;
context.fillRect(-5,-50,10,50);
// 垂直渐变 一作用树冠在树干上的投影
let shadow = context.createLinearGradient(0,-50,0,0);//垂直渐变
// 投影渐变的起点是透明度设为50%的黑色
shadow.addColorStop(0,'rgba(0,0,0,0.5)');
// 方向垂直向下,渐变色在很短的距离内迅速渐变至完全透明,这段长度之外的树干上没有投影
shadow.addColorStop(0.2,'rgba(0,0,0,0.0)');
// 在树干上填充投影渐变
context.fillStyle=shadow;
context.fillRect(-5,-50,10,50);
}
// 绘制可复制的小树 树冠
function createCanCopyTree(context){
treeCrown(context);
treeCrownFill(context);
treeTrunk(context);
}
// 绘制(小树)
function drawTree(context,translateX,translateY,scaleX,scaleY){
context.save();
context.translate(translateX,translateY);
if(scaleX && scaleY){
context.scale(scaleX,scaleY);
}
// 先画阴影,再画树,如果顺序反过来,阴影则会覆盖树
treeShadow(context);
createCanCopyTree(context);
context.restore();
}
function treeShadow(context){
context.save();
// 变换开始(加阴影)
context.transform(1,0,-0.5,1,0,0);
// y轴方向压缩为原来的60%
context.scale(1,0.6);
context.fillStyle='rgba(0,0,0,0.2)';
context.fillRect(-5,-50,10,50);
treeCrown(context);
context.fill();// 阴影只填充,不描边
context.restore();
}
// 绘制林荫小路
function drawRoad(context){
context.save();
context.translate(-10,350);
context.beginPath();
context.moveTo(0,0);
context.quadraticCurveTo(170,-50,260,-190);
context.quadraticCurveTo(310,-250,510,-250);
context.strokeStyle='#663300';
context.lineWidth=20;
context.stroke();
context.restore();
}
// 绘制石头小路
function drawStoneRoad(context,stone){
context.save();
context.translate(-10,350);
context.beginPath();
context.moveTo(0,0);
context.quadraticCurveTo(170,-50,260,-190);
context.quadraticCurveTo(310,-250,510,-250);
// strokeStyle 不仅可以是颜色 还可以是背景图
context.strokeStyle=context.createPattern(stone,'repeat');
context.lineWidth=30;
context.stroke();
context.restore();
}
// 使用文本
function typeText(context,text){
context.save();
// 字号为60px 字体为impact
context.font="60px impact";
context.fillStyle='#996600';
context.textAlign='center';
// 文字的阴影
context.shadowColor='rgba(0,0,0,0.2)';
context.shadowOffsetX=15;
context.shadowOffsetY=-10;
context.shadowBlur=2;//轻微模糊
context.fillText(text,250,60,400);
context.restore();
}
// 通过指定不同的偏移量,在不同的位置画小树
//drawTree(ctx,130,150);
drawTree(ctx,110,250);
// 画小路
//drawRoad(ctx);
// 画石头小路 图片加载成功后再画石头小路
let stone = new Image();
stone.src='stone.png';
stone.onload=function(){
drawStoneRoad(ctx,stone);
drawTree(ctx,300,350,2,2);//画完小路再画路另一侧的树,防止路把树遮挡
}
typeText(ctx,'Happy Trails!');
</script>
</body>
</html>
参考文档:《HTML5程序设计(第2版)》