组合
画图,文字,图形,本章会讲解动画,再canvas上执行动画效果,在动画之前可以先看一下组合,之前画图都总是将一个图形画在另一个之上,对于其他更多的情况,仅仅这样是远远不够的。比如,对合成的图形来说,绘制顺序会有限制。现在可以使用
globalCompositeOperation来改变这种情况,还可以使用clip隐藏我们不想看到的地方,不同于之前的clearRect,清除地方仅限制矩形
globalCompositeOperation
这个属性设定了在画新图形时采用的遮盖策略,其值是一种遮盖方式的字符串
-
source-over:这个是默认属性,也就是再现有的图上绘制上下文。
-
source-in:新图形只在新图形和目标画布重叠的地方绘制,其它地方都是透明的
-
source-out:在不与现有画布内容重叠的地方绘制新图形
-
source-atop: 新图形只在与现有画布内容重叠的地方绘制。
-
destination-over:在现有的画布内容后面绘制新的图形。
-
destination-in:现有的画布内容保持在新图形和现有画布内容重叠的位置。其他的都是透明的。
-
destination-out: 现有内容保持在新图形不重叠的地方(重叠的话就会变为透明)。
-
destination-atop:现有的画布只保留与新图形重叠的部分,新的图形是在画布内容后面绘制的。
-
lighter: 两个重叠图形的颜色是通过颜色值相加来确定的。
-
copy: 只显示新图形。
-
xor:图像中,那些重叠和正常绘制之外的其他地方是透明的。
-
multiply: 将顶层像素与底层相应像素相乘。
-
screen:像素被倒转,相乘,再倒转,得到图像
-
overlay:multiply 和 screen 的结合,原本暗的地方更暗,原本亮的地方更亮。
-
darken:保留两个图层中最暗的像素。
-
lighten:保留两个图层中最亮的像素。
-
color-dodge: 将底层除以顶层的反置。
-
color-burn:将反置的底层除以顶层,然后将结果反过来。
-
hard-light:屏幕相乘(A combination of multiply and screen)类似于叠加,但上下图层互换了。
-
soft-light:用顶层减去底层或者相反来得到一个正值。
-
difference:一个柔和版本的强光(hard-light)。纯黑或纯白不会导致纯黑或纯白。
-
exclusion:对比度比较低和difference相比
-
hue:保留了底层的亮度和色度,同时采用了顶层的色调(hue)。
-
saturation:保留底层的亮度和色调,同时采用顶层的色度。
-
color:保留了底层的亮度,同时采用了顶层的色调和色度
-
luminosity:保持底层的色调和色度,同时采用顶层的亮度
clip(裁剪)
我们使用 clip()方法来创建一个新的裁切路径。默认情况下,canvas 有一个与它自身一样大的裁切路径(也就是没有裁切效果)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
#canvas {
border: 1px solid #000;
}
</style>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
<script>
const canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.fillRect(0, 0, 150, 150);
ctx.translate(75, 75);
// Create a circular clipping path
ctx.beginPath();
ctx.arc(0, 0, 70, 0, Math.PI * 2, true);
ctx.clip();
// draw background
var lingrad = ctx.createLinearGradient(0, -75, 0, 75);
lingrad.addColorStop(0, "red");
lingrad.addColorStop(1, "pink");
ctx.fillStyle = lingrad;
ctx.fillRect(-75, -75, 150, 150);
ctx.fillStyle = "#22a4f1";
ctx.beginPath();
ctx.moveTo(50, 0);
ctx.lineTo(50, 50);
ctx.lineTo(130, 30);
ctx.fill()
</script>
</body>
</html>
上面代码中呢我绘制了一个剪裁区域,在剪裁区域呢有个三角形,三角形超出剪裁区域的就会看不见,只有剪裁区域内的才可以被看见
基础动画
你可以通过以下的步骤来画出一帧:
- 清空 canvas 除非接下来要画的内容会完全充满 canvas(例如背景图),否则你需要清空所有。最简单的做法就是用
clearRect方法。 - 保存 canvas 状态 如果你要改变一些会改变 canvas 状态的设置(样式,变形之类的),又要在每画一帧之时都是原始状态的话,你需要先保存一下。
- 绘制动画图形(animated shapes) 这一步才是重绘动画帧。
- 恢复 canvas 状态 如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
#canvas {
border: 1px solid #000;
}
</style>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
<script>
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// 设置三张图片
var sun = new Image();
var moon = new Image();
var earth = new Image();
function init () {
sun.src = "https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/640b4df0a5074e9a9bf4777fdf1fd74e~tplv-k3u1fbpfcp-watermark.image";
moon.src = "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/05bc3992bd5044448f029b7d68049b38~tplv-k3u1fbpfcp-watermark.image";
earth.src = " https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f4ad71733e934b818a52bcfea56a683f~tplv-k3u1fbpfcp-watermark.image";
window.requestAnimationFrame(draw);
}
function draw () {
var ctx = document.getElementById("canvas").getContext("2d");
// 设置遮盖策略,再现有的画布后面进行绘制
ctx.globalCompositeOperation = "destination-over";
ctx.clearRect(0, 0, 300, 300);
// 初始化样式
ctx.fillStyle = "rgba(0,0,0,0.4)";
ctx.strokeStyle = "rgba(0,153,255,0.4)";
// 记录初始化样式
ctx.save();
// 移动到画布中间
ctx.translate(150, 150);
var time = new Date();
// 每秒旋转角度*当前分钟
// (2 * Math.PI) / 60) * time.getSeconds()
// 每毫秒旋转角度*当前毫秒
// ((2 * Math.PI) / 60000) * time.getMilliseconds(),
ctx.rotate(
((2 * Math.PI) / 60) * time.getSeconds() +
((2 * Math.PI) / 60000) * time.getMilliseconds(),
);
ctx.translate(105, 0);
ctx.fillRect(0, -12, 50, 24);
// 绘图图片
ctx.drawImage(earth, -12, -12);
// 同上一样
ctx.save();
ctx.rotate(
((2 * Math.PI) / 6) * time.getSeconds() +
((2 * Math.PI) / 6000) * time.getMilliseconds(),
);
ctx.translate(0, 28.5);
ctx.drawImage(moon, -3.5, -3.5);
ctx.restore();
ctx.restore();
ctx.beginPath();
ctx.arc(150, 150, 105, 0, Math.PI * 2, false);
ctx.stroke();
ctx.drawImage(sun, 0, 0, 300, 300);
// 每帧执行一次
window.requestAnimationFrame(draw);
}
init();
</script>
</body>
</html>
上面是一个简单的太阳系动画,主要就是先设置起始位置,然后算出来每秒每毫秒的旋转角度,进行移动,然后使用requestAnimationFrame执行连续执行
下面实现一个全景照片的自动展示,使用定时器来展示。注释写的比较清除,这里就不多陈述了,主要也就是一些计算和判断
跳动的小球,是基于刷新率来进行计算的即,代码中会有很清楚的注释,可以根据注释来看,这里就不过多进行陈述了。