2D转换
css3转换
CSS3 转换可以对元素进行移动、缩放、转动、拉长或拉伸。
工作原理
转换的效果是让某个元素改变形状,大小和位置。
您可以使用 2D 或 3D 转换来转换您的元素。
2D变换方法
- translate()
- rotate()
- scale()
- skew()
- matrix()
translate()方法
translate()方法,根据左(X轴)和顶部(Y轴)位置给定的参数,从当前元素位置移动。
style>
#trans{
height: 200px;
width: 200px;
background-color: red;
transform: translate(100px);
}
</style>
<body>
<div id="trans">2d效果转换</div>
</body>
rotate()方法
rotate()方法,在一个给定度数顺时针旋转的元素。负值是允许的,这样是元素逆时针旋转。
#trans{
height: 200px;
width: 200px;
background-color: red;
transform: translate(100px);
transform: rotate(180deg);//deg表示角度
}
</style>
<body>
<div id="trans">2d效果转换</div>
</body>
scale方法
scale()方法,该元素增加或减少的大小,取决于宽度(X轴)和高度(Y轴)的参数:
#scale{
height: 100px;
width: 100px;
background-color: blue;
transform: scale(2,2);
margin: auto;
}
<div id="scale">放大缩小</div>
scale(2,3)转变宽度为原来的大小的2倍,和其原始大小3倍的高度。
tips:scale() 中参数为-1时,图像反向
skew()方法
语法
transform:skew(<angle [,<angle>]>);
包含两个参数值,分别表示X轴和Y轴倾斜的角度,如果第二个参数为空,则默认为0,参数为负表示向相反方向倾斜。
- skewX();表示只在X轴(水平方向)倾斜。
- skewY();表示只在Y轴(垂直方向)倾斜。
#skew{
transform: skew(30deg,20deg);
}
<div id="skew">倾斜</div>
matrix()方法
matrix()方法和2D变换方法合并成一个。
matrix 方法有六个参数,包含旋转,缩放,移动(平移)和倾斜功能。
#matrix{
transform:matrix(0.866,0.5,-0.5,0.866,0,0);
}
<div id="matrix">matrix</div>
matrix 的参数表示matrix( scaleX(), skewY(), skewX(), scaleY(), translateX(), translateY() )
matrix() 不支持角度值deg
所有2D转换方法
函数 | 描述 |
---|
matrix(n,n,n,n,n,n) | 定义 2D 转换,使用六个值的矩阵。 | translate(x,y) | 定义 2D 转换,沿着 X 和 Y 轴移动元素。 | translateX(n) | 定义 2D 转换,沿着 X 轴移动元素。 | translateY(n) | 定义 2D 转换,沿着 Y 轴移动元素。 | scale(x,y) | 定义 2D 缩放转换,改变元素的宽度和高度。 | scaleX(n) | 定义 2D 缩放转换,改变元素的宽度。 | scaleY(n) | 定义 2D 缩放转换,改变元素的高度。 | rotate(angle) | 定义 2D 旋转,在参数中规定角度。 | skew(x-angle,y-angle) | 定义 2D 倾斜转换,沿着 X 和 Y 轴。 | skewX(angle) | 定义 2D 倾斜转换,沿着 X 轴。 | skewY(angle) | 定义 2D 倾斜转换,沿着 Y 轴。 |
3D转换
CSS3 允许您使用 3D 转换来对元素进行格式化。
在本章中,您将学到其中的一些 3D 转换方法:
rotate()方法
rotateX()方法,围绕其在一个给定度数X轴旋转的元素。
#rotate{
transform: rotateX(120deg);
}
<div id="rotate">3dX旋转</div>
rotateY()方法,围绕其在一个给定度数Y轴旋转的元素。
#rotateY{
transform: rotateY(120deg);
}
<div id="rotateY">3d旋转2</div>
转换属性
3D转换方法
函数 | 描述 |
---|
matrix3d(n,n,n,n,n,n, n,n,n,n,n,n,n,n,n,n) | 定义 3D 转换,使用 16 个值的 4x4 矩阵。 | translate3d(x,y,z) | 定义 3D 转化。 | translateX(x) | 定义 3D 转化,仅使用用于 X 轴的值。 | translateY(y) | 定义 3D 转化,仅使用用于 Y 轴的值。 | translateZ(z) | 定义 3D 转化,仅使用用于 Z 轴的值。 | scale3d(x,y,z) | 定义 3D 缩放转换。 | scaleX(x) | 定义 3D 缩放转换,通过给定一个 X 轴的值。 | scaleY(y) | 定义 3D 缩放转换,通过给定一个 Y 轴的值。 | scaleZ(z) | 定义 3D 缩放转换,通过给定一个 Z 轴的值。 | rotate3d(x,y,z,angle) | 定义 3D 旋转。 | rotateX(angle) | 定义沿 X 轴的 3D 旋转。 | rotateY(angle) | 定义沿 Y 轴的 3D 旋转。 | rotateZ(angle) | 定义沿 Z 轴的 3D 旋转。 | perspective(n) | 定义 3D 转换元素的透视视图。 |
CSS3过度
CSS3中,我们为了添加某种效果可以从一种样式转变到另一个的时候,无需使用Flash动画或JavaScript。
原理
CSS3 过渡是元素从一种样式逐渐改变为另一种的效果。
要实现这一点,必须规定两项内容:
注意: 如果未指定的期限,transition将没有任何效果,因为默认值是0。
指定的CSS属性的值更改时效果会发生变化。一个典型CSS属性的变化是用户鼠标放在一个元素上时:
实例
div
{
width:100px;
height:100px;
background:red;
transition-property:width;
transition-duration:1s;
transition-timing-function:linear;
transition-delay:2s;
}
div:hover
{
width:200px;
}
所有过渡属性
CSS3动画
CSS3 可以创建动画,它可以取代许多网页动画图像、Flash 动画和 JavaScript 实现的效果。
@keyframes规则
要创建 CSS3 动画,你需要了解 @keyframes 规则。
@keyframes 规则是创建动画。
@keyframes 规则内指定一个 CSS 样式和动画将逐步从目前的样式更改为新的样式。
CSS3动画
当在 @keyframes 创建动画,把它绑定到一个选择器,否则动画不会有任何效果。
指定至少这两个CSS3的动画属性绑定向一个选择器:
定义动画
/* 动画效果创建 */
@keyframes fade {
from {
background: red;
}
to {
background: yellow;
}
}
在id选择中使用上面创建的动画
#animate {
animation: fade 5s;
}
案例2
可以使用百分比来定义动画播放的时间戳,或者用from和to
这里推荐使用百分比
@keyframes fadePercent {
0%{background: black;}
25%{background: blue;}
50%{background: red;}
100%{background: white;}
}
案例3
更改渐变的方向和位置
0% {background: red; left:0px; top:0px;}
25% {background: yellow; left:200px; top:0px;}
50% {background: blue; left:200px; top:200px;}
75% {background: green; left:0px; top:200px;}
100% {background: red; left:0px; top:0px;}
动画属性
动画函数
简介
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。 根据以上 MDN 的定义,requestAnimationFrame 是浏览器提供的一个按帧对网页进行重绘的 API 。
<style>
.test{
width: 100px;
height: 100px;
background-color: aquamarine;
}
</style>
<body>
<div class="test"></div>
</body>
<script>
const test = document.querySelector(".test");
let i = 0;
function animation() {
if (i > 200) return;
test.style.marginLeft = `${i}px`;
window.requestAnimationFrame(animation);
i++;
}
window.requestAnimationFrame(animation);
</script>
上面的代码 1s 大约执行 60 次,因为一般的屏幕硬件设备的刷新频率都是 60Hz ,然后每执行一次大约是 16.6ms 。使用 requestAnimationFrame 的时候,只需要反复调用它就可以实现动画效果。
同时 requestAnimationFrame 会返回一个请求 ID,是回调函数列表中的一个唯一值,可以使用 cancelAnimationFrame 通过传入该请求 ID 取消回调函数。
const test = document.querySelector(".test");
let i = 0;
let requestId;
function animation() {
test.style.marginLeft = `${i}px`;
requestId = requestAnimationFrame(animation);
console.log(requestId);
i++;
if (i > 200) {
cancelAnimationFrame(requestId);
}
}
window.requestAnimationFrame(animation);
但是使用setTimeout 要比动画函数要卡顿许多,所以尽量使用动画函数制作动画。
3D动画
3D转换属性
属性 | 描述 |
---|
transform | 向元素应用 2D 或 3D 转换。 | transform-origin | 允许你改变被转换元素的位置。 | transform-style | 规定被嵌套元素如何在 3D 空间中显示。 | perspective | 规定 3D 元素的透视效果。 | perspective-origin | 规定 3D 元素的底部位置。 | backface-visibility | 定义元素在不面对屏幕时是否可见。 |
函数 | 描述 |
---|
matrix3d(n,n,n,n,n,n, n,n,n,n,n,n,n,n,n,n) | 定义 3D 转换,使用 16 个值的 4x4 矩阵。 | translate3d(x,y,z) | 定义 3D 转化。 | translateX(x) | 定义 3D 转化,仅使用用于 X 轴的值。 | translateY(y) | 定义 3D 转化,仅使用用于 Y 轴的值。 | translateZ(z) | 定义 3D 转化,仅使用用于 Z 轴的值。 | scale3d(x,y,z) | 定义 3D 缩放转换。 | scaleX(x) | 定义 3D 缩放转换,通过给定一个 X 轴的值。 | scaleY(y) | 定义 3D 缩放转换,通过给定一个 Y 轴的值。 | scaleZ(z) | 定义 3D 缩放转换,通过给定一个 Z 轴的值。 | rotate3d(x,y,z,angle) | 定义 3D 旋转。 | rotateX(angle) | 定义沿 X 轴的 3D 旋转。 | rotateY(angle) | 定义沿 Y 轴的 3D 旋转。 | rotateZ(angle) | 定义沿 Z 轴的 3D 旋转。 | perspective(n) | 定义 3D 转换元素的透视视图。 |
Canvas入门
简介
Canvas 中文名叫 “画布”,是 HTML5 新增的一个标签。
Canvas 允许开发者通过 JS 在这个标签上绘制各种图案。
Canvas 拥有多种绘制路径、矩形、圆形、字符以及图片的方法。
Canvas 在某些情况下可以 “代替” 图片。
Canvas 可用于动画、游戏、数据可视化、图片编辑器、实时视频处理等领域。
Canvas和SVG矢量图的区别
Canvas | SVG |
---|
用JS动态生成元素(一个HTML元素) | 用XML描述元素(类似HTML元素那样,可用多个元素来描述一个图形) | 位图(受屏幕分辨率影响) | 矢量图(不受屏幕分辨率影响) | 不支持事件 | 支持事件 | 数据发生变化需要重绘 | 不需要重绘 |
如果需要的数据量比较大,使用Canvas 较好,如果对于清晰度有要求并且交互操作比较多,使用SVG 比较好
使用canvas
绘制一个直线
<style>
.printer{
border: 1px solid #e1e1e1;
}
</style>
<body>
<!-- 画布 -->
<canvas class="printer" width="300px" height="300px">
</canvas>
</body>
<script>
let ptr=document.querySelector('.printer')
let con=ptr.getContext('2d')
con.moveTo(100,100)//绘制起点
con.lineTo(200,200)//绘制终点
con.stroke()//连接起点和终点
console.log(ptr.width)//300
console.log(ptr.height)//300
</script>
canvas 的宽高
canvas 画布的宽高默认为300px,150px。如果要设置宽高,在style 中设置是不行的,canvas 自带属性width 和height ,设置宽高时可以不带单位
<canvas class="printer" width="300" height="300">
</canvas>
如果用css 设置宽高,会出现内容被拉伸的状况
canvas 的默认宽度是300px,默认高度是150px。
- 如果使用
css 修改 canvas 的宽高(比如变成 400px * 400px),那宽度就由 300px 拉伸到 400px,高度由 150px 拉伸到 400px。 - 使用
js 获取 canvas 的宽高,此时返回的是 canvas 的默认值。
线条的默认宽度和颜色
线条的默认宽度是 1px ,默认颜色是黑色。
但由于默认情况下 canvas 会将线条的中心点和像素的底部对齐,所以会导致显示效果是 2px 和非纯黑色问题。
坐标系
Canvas 使用的是W3C 坐标系:从上往下,从左往右。
W3C 坐标系 和 数学直角坐标系 的 X轴 是一样的,只是 Y轴 的反向相反。
W3C 坐标系 的 Y轴 正方向向下。
线条的样式设置
lineWidth :线的粗细strokeStyle :线的颜色lineCap :线帽:默认: butt ; 圆形: round ; 方形: square
//修改直线的宽度,颜色
con.lineWidth = 3;
con.strokeStyle = "#f00";
// 修改直线两端样式
con.lineCap = "round"; // 默认: butt; 圆形: round; 方形: square
beginPath()
在绘制多条线段时,要是设置线条样式,会将其他的不需要样式的线条“污染”
所以使用beginPath() 另开一条线段。这样就不会污染了。
con.moveTo(100, 100); //绘制起点
con.lineTo(200, 200); //绘制终点
//修改直线的宽度,颜色
con.lineWidth = 3;
con.strokeStyle = "#f00";
// 修改直线两端样式
con.lineCap = "round"; // 默认: butt; 圆形: round; 方形: square
con.stroke(); //连接起点和终点
con.beginPath();
con.moveTo(50, 20);
con.lineTo(150, 20);
con.strokeStyle='pink'//后面的线段不会影响前面的线段
con.stroke();
折线
折线和直线一样,只不过需要多段moveTo() 和lineTo()
//新画布
let pub=document.querySelector('.canva')
let can=pub.getContext('2d')
// 折线
can.beginPath()
can.lineWidth=4
can.strokeStyle='blue'
can.moveTo(30,30)
can.lineTo(100,100)
can.lineTo(150,100)
can.lineTo(200,200)
can.stroke()
矩形
可以根据折线的原理来进行绘制矩形
//矩形
can.beginPath()
can.lineWidth=5
can.strokeStyle='#f00'
can.moveTo(100,300)
can.lineTo(300,300)
can.lineTo(300,400)
can.lineTo(100,400)
can.closePath()//闭合线段,或者使用can.lineTo(起始位置),推荐使用closePath()
can.stroke()
根据canvas 提供的rect() 方法绘制矩形
strokeStyle :设置描边的属性(颜色、渐变、图案)
strokeRect(x, y, width, height) :描边矩形(x和y是矩形左上角起点;width 和 height 是矩形的宽高)
strokeStyle 必须写在 strokeRect() 前面,不然样式不生效。
//使用strokeRect()绘制矩形
can.beginPath()
can.strokeStyle='blue'
can.lineWidth=3
can.strokeRect(100,300,200,100)//起点的x,y,宽,高
可以使用fillRect() 来填充矩形的颜色,并且此方法和strokeRect() 功能差不多,同时fillstyle() 可以填充颜色,并且要写在fillRect() 上面,不然不会生效
can.beginPath()
can.strokeStyle='blue'
can.fillStyle='pink'
can.lineWidth=3
can.fillRect(100,300,200,100)//绘制矩形并进行填充颜色,默认为黑色,可以使用fillStyle进行更改
// can.strokeRect(100,300,200,100)//起点的x,y,宽,高
同时使用fillRect() 和strokeRect()
两个方法同时使用可以有边框+填充的效果
//两个方法同时使用(fillRect()和strokeRect())
can.beginPath()
can.strokeStyle='#f00'
can.strokeRect(100,300,200,100)
can.fillStyle='#00f'
can.fillRect(100,300,200,100)
rect() 绘制矩形
rect() 和 fillRect() 、strokeRect() 的用法差不多,唯一的区别是:
strokeRect() 和 fillRect() 这两个方法调用后会立即绘制;rect() 方法被调用后,不会立刻绘制矩形,而是需要调用 stroke() 或 fill() 辅助渲染。
//rect()绘制矩形
can.beginPath();
can.strokeStyle = "red";
can.fillStyle = "pink";
can.rect(100, 300, 200, 100);
can.stroke();
can.fill()
rect() +stroke() =strokeRect()
rect() +fill() =fillRect()
clearRect() 清空矩形
语法:
clearRect(x,y,width,height)
can.clearRect(150,350,50,50)//清空指定区域的矩形,x,y,width,height
可以通过此方法进行画布的清除
can.clearRect(0,0,can.width,can.height)
Canvas多边形制作
多边形的制作
三角形
Canvas 要画多边形,需要使用 moveTo() 、 lineTo() 和 closePath() 。
//三角型制作
con.beginPath(); //新路径生成
con.lineWidth = 4;
con.strokeStyle = "blue";
con.moveTo(10, 10);
con.lineTo(50, 10);
con.lineTo(50, 50);
con.closePath();
con.stroke();
如果使用lineTo 去闭合三角形的话,有时候会不完全闭合,使用closePath() 的话就不会产生这种问题。
菱形
//菱形制作
con.beginPath()
con.lineWidth=5
con.strokeStyle='#f00'
con.moveTo(120,20)
con.lineTo(100,50)
con.lineTo(120,80)
con.lineTo(140,50)
con.closePath()
con.stroke()
圆形
使用arc() 方法进行圆形的制作
arc(x, y, r, sAngle, eAngle,counterclockwise)
x 和 y : 圆心坐标r : 半径sAngle : 开始角度eAngle : 结束角度counterclockwise : 绘制方向(true: 逆时针; false: 顺时针),默认 false
开始角度和结束角度,都是以弧度为单位。例如 180°就写成 Math.PI ,360°写成 Math.PI * 2 ,以此类推。
在实际开发中,为了让自己或者别的开发者更容易看懂弧度的数值,1°应该写成 Math.PI / 180 。
- 100°:
100 * Math.PI / 180 - 110°:
110 * Math.PI / 180 - 241°:
241 * Math.PI / 180
注意:绘制圆形之前,必须先调用 beginPath() 方法!!! 在绘制完成之后,还需要调用 closePath() 方法!!!
半圆
在制作圆形的时候,画到180° 时进行闭合就是半圆
//半圆制作
con.beginPath()
con.lineWidth=3
con.strokeStyle=`#f00`
con.arc(500,200,180,0*Math.PI/180,180*Math.PI/180,false)
con.closePath()
con.stroke()
如果想让半圆在上方呈现,将最后一个参数改为true
//上方半圆制作
con.beginPath()
con.lineWidth=3
con.strokeStyle=`#f00`
con.arc(500,200,180,0*Math.PI/180,180*Math.PI/180,true)
con.closePath()
con.stroke()
弧线
可以使用arc() 画弧线,选择弧度后,不进行closePath() 就可以画出弧线
//arc()弧线
con.beginPath()
con.lineWidth=3
con.strokeStyle=`#f00`
con.arc(200,400,180,0*Math.PI/180,30*Math.PI/180,false)
con.stroke()
arcTo() 画弧线
arcTo(cx, cy, x2, y2, radius)
cx : 两切线交点的横坐标cy : 两切线交点的纵坐标x2 : 结束点的横坐标y2 : 结束点的纵坐标radius : 半径
其中,(cx, cy) 也叫控制点,(x2, y2) 也叫结束点。
是不是有点奇怪,为什么没有 x1 和 y1 ?
(x1, y1) 是开始点,通常是由 moveTo() 或者 lineTo() 提供。
arcTo() 方法利用 开始点、控制点和结束点形成的夹角,绘制一段与夹角的两边相切并且半径为 radius 的圆弧。
//arcTo()弧线
con.moveTo(40, 40);
con.arcTo(120, 40, 120, 120, 80);//切线交点的x,y;结束点的横纵坐标;半径
con.stroke();
基本样式
stroke()
stroke() 主要是用来描边的
lineWidth
lineWidth 用来控制线的宽度,默认是1默认单位为px
strokeStyle
线条样式,可以设置颜色
strokeStyle=颜色值
lineCap
线帽样式,可以设置线两端的样式
lineCap='属性值'
butt : 默认值,无线帽square : 方形线帽round : 圆形线帽
使用 square 和 round 的话,会使线条变得稍微长一点点,这是给线条增加线帽的部分,这个长度在日常开发中需要注意。
线帽只对线条的开始和结尾处产生作用,对拐角不会产生任何作用。
拐角样式lineJoin
lineJoin = '属性值'
属性值包括:
miter : 默认值,尖角round : 圆角bevel : 斜角
虚线样式
setLineDash([])
里面的参数是一个数组了类型。
可以传入1-3 个以上的参数
cxt.setLineDash([10]) // 只传1个参数,实线与空白都是 10px
cxt.setLineDash([10, 20]) // 2个参数,此时,实线是 10px, 空白 20px
cxt.setLineDash([10, 20, 5]) // 传3个以上的参数,此例:10px实线,20px空白,5px实线,10px空白,20px实线,5px空白 ……
此外,还可以始终 cxt.getLineDash() 获取虚线不重复的距离;
用 cxt.lineDashOffset 设置虚线的偏移位。
填充
使用fill() 进行填充图形,默认填充为黑色。使用fillStyle 来设置填充颜色。
非零环绕填充
在使用 fill() 方法填充时,需要注意一个规则:非零环绕填充。
在使用 moveTo 和 lineTo 描述图形时,如果是按顺时针绘制,计数器会加1;如果是逆时针,计数器会减1。
当图形所处的位置,计数器的结果为0时,它就不会被填充。
比如
// 外层矩形
cxt.moveTo(50, 50)
cxt.lineTo(250, 50)
cxt.lineTo(250, 250)
cxt.lineTo(50, 250)
cxt.closePath()
// 内层矩形
cxt.moveTo(200, 100)
cxt.lineTo(100, 100)
cxt.lineTo(100, 200)
cxt.lineTo(200, 200)
cxt.closePath()
cxt.fill()
外层矩形是顺时针绘制的,内层矩形是逆时针绘制的,顺时针绘制的大矩形里面的计数器值为1,而里面又是由一个逆时针绘制的小矩形,小矩形里面的值是-1 而加上外层矩形里面的计数器值1,抵消为0 所以内层矩形的里面不会被填充。
canvas文本
样式font
canvas也可以和css一样进行字体的设置
cxt.font = 'font-style font-variant font-weight font-size/line-height font-family'
如果需要设置字号 font-size ,需要同时设置 font-family 。
cxt.font = '30px 宋体'
描边storkText()
使用 strokeText() 方法进行文本描边
语法:
strokeText(text, x, y, maxWidth)
text : 字符串,要绘制的内容x : 横坐标,文本左边要对齐的坐标(默认左对齐)y : 纵坐标,文本底边要对齐的坐标maxWidth : 可选参数,表示文本渲染的最大宽度(px),如果文本超出 maxWidth 设置的值,文本会被压缩。
//文本
con.font='40px 宋体'
con.strokeText('hello',10,50)
strokeStyle
strokeStyle 设置字体颜色
fillText 填充文本
fillText(text,x,y,maxWidth)
con.fillText('hello',20,60)
获取文本长度
使用measureText() 获取文本的长度,单位是px
console.log(con.measureText('hello').width)//100
水平对齐
使用textAlign 对齐文本
使用 textAlign 属性可以设置文字的水平对齐方式,一共有5个值可选
start : 默认。在指定位置的横坐标开始。end : 在指定坐标的横坐标结束。left : 左对齐。right : 右对齐。center : 居中对齐。
垂直对齐textBaseline
使用 textBaseline 属性可以设置文字的垂直对齐方式。
textBaseline 可选属性:
alphabetic : 默认。文本基线是普通的字母基线。top : 文本基线是 em 方框的顶端。bottom : 文本基线是 em 方框的底端。middle : 文本基线是 em 方框的正中。hanging : 文本基线是悬挂基线。
canvas图片
渲染图片
渲染图片的方式有2中,一种是在JS里加载图片再渲染,另一种是把DOM里的图片拿到 canvas 里渲染。
JS渲染:
drawImage(image,dx,dy)
DOM渲染:
<canvas width="800px" height="500"></canvas>
<img src="../4.(工具)css3动画效果/src/plane.svg" alt="" srcset="" class="img">
//DOM在canvas上渲染图片
let image=document.querySelector('.img')
con.drawImage(image,30,30)
DOM渲染时,将img 标签设置display:none 防止它影响canvas
设置图片尺寸
drawImg(img,dx,dy,dw,dh)
dw :宽度
dh :高度
let image=document.querySelector('.img')
con.drawImage(image,30,30,200,200)
截取图片
截图图片同样使用drawImage() 方法,只不过传入的参数数量比之前都多,而且顺序也有点不一样了。
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
以上参数缺一不可
image : 图片对象sx : 开始截取的横坐标sy : 开始截取的纵坐标sw : 截取的宽度sh : 截取的高度dx : 图片左上角的横坐标位置dy : 图片左上角的纵坐标位置dw : 图片宽度dh : 图片高度
//截取图片
con.drawImage(image,40,40,100,100,30,30,200,300)
canvas变形
状态保存和恢复
在变形之前,两个很重要的方法
save() 保存当前canvas画布的状态
restore() 恢复到之前保存过的状态
Canvas 状态存储在栈中,每当save() 方法被调用后,当前的状态就被推送到栈中保存。可以调用任意多次 save 方法。每一次调用 restore 方法,上一个保存的状态就从栈中弹出,所有设定都恢复。
//创建一个新的路径
con.beginPath()
con.fillStyle='#f00'
//画一个高宽为200的矩形,并在画布中间呈现
con.fillRect(300,150,200,200)
con.fill()
con.save()//保存当前状态,默认状态
save()和restore()方法只会在有效范围内生效,它是绘制状态的存储器,并不是画布内容的存储器。它是基于状态记录的。 Canvas Context维持着绘制状态的堆栈。区分绘制状态:
- 当前矩阵变换
例如:平移translate(),缩放scale(),以及旋转rotate()等 - 剪切区域
clip() - 以下属性值:
strokeStyle,fillStyle,globalAlpha,lineWidth,lineCap,lineJoin,miterLimit,shadowOffsetX,shadowOffsetY,shadowBlur,shadowColor,globalCompositeOperation,font,textAlign,textBaseline。 - 路径和位图不是绘图状态的一部分,使用
save()和restore() 不会生效。路径是持久的,只能使用beginPath()方法重置,位图是画布的属性,而非上下文。 context.save() 将当前状态压入堆栈。context.restore() 弹出堆栈上的顶级状态,将上下文恢复到该状态。
translating
语法
con.translate(dx,dy)
dx 指横坐标偏移量,dy 指的是纵坐标的偏移量
//加载上一次的状态
con.restore()
//在上一次状态中改变了位置和颜色
con.fillStyle='#0f0'
con.translate(100,-100)//向右移动100.向上移动100
con.fillRect(300,150,200,200)
rotate方法
rotate() 表示旋转,默认旋转的圆心为圆点,如果要改变圆点,需要借助translate()
con.restore()
con.rotate((Math.PI*2)/9)//旋转60度
con.fillRect(300,150,200,200)
通过rotate() 进行中心旋转
//第二块画布
let canva=document.querySelector('.canva')
let print=canva.getContext('2d')
print.save()//保存画布的初始状态
print.translate(250,250)//圆点位移
print.beginPath()
print.fillStyle='#0f0'
print.rotate((Math.PI*2)/6)
print.fillRect(-25,-25,50,50)
scale方法
scale 方法表示缩放
画布初始情况下,是以左上角坐标为原点的第一象限。如果参数为负实数,相当于以 x 或 y 轴作为对称轴镜像反转(例如,使用translate(0,canvas.height); scale(1,-1); 以 y 轴作为对称轴镜像反转,就可得到著名的笛卡尔坐标系,左下角为原点)。
默认情况下,canvas 的 1 个单位为 1 个像素。举例说,如果我们设置缩放因子是 0.5,1 个单位就变成对应 0.5 个像素,这样绘制出来的形状就会是原先的一半。同理,设置为 2.0 时,1 个单位就对应变成了 2 像素,绘制的结果就是图形放大了 2 倍。
print.restore()//恢复初始态
print.font='36px 宋体'
print.scale(2,2)
print.fillText('hello',50,50)
像素操作
imageData对象
图像本质是由像素构成的,在canvas 下的图像像素都保存在imageData 对象下。
ImageData() 构造函数返回一个新的实例化的 ImageData 对象, 此对象由给定的类型化数组和指定的宽度与高度组成
new ImageData([array], width, height);
/*
array
包含图像隐藏像素的 Uint8ClampedArray 数组。如果数组没有给定,指定大小的黑色矩形图像将会被创建。
width
无符号长整型(unsigned long)数值,描述图像的宽度。
height
无符号长整型(unsigned long)数值,描述图像的高度。
*/
其中的属性都是只读属性,不可以改变
创建ImageData对象
使用createImageData() 方法创建一个ImageData对象
var myImageData = ctx.createImageData(width, height);
创建一个具体尺寸的ImageData对象,所有像素被预设为透明黑
或者可以这样创建
var myImageData = ctx.createImageData(anotherImageData);
得到像素数据
使用getImageData() 方法来获取指定区域的像素数据,它返回的也是一个ImageData() 对象
var myImageData = ctx.getImageData(left, top, width, height);
实例
//鼠标移入事件
canvas.addEventListener('mousemove',e=>{
//获取当前像素位置(鼠标移入的位置)
let x=e.offsetX
let y=e.offsetY
let pixel=con.getImageData(x,y,1,1)//获取鼠标移入位置的一像素对象,并存到pixel变量
//设置显示的位置
hoverDiv.style.background=`rgba(${pixel.data[0]}, ${pixel.data[1]}, ${pixel.data[2]}, 1)`
hoverDiv.textContent=`rgba(${pixel.data[0]}, ${pixel.data[1]}, ${pixel.data[2]}, 1)`
})
canvas.addEventListener('click',e=>{
//获取当前像素位置(鼠标点击的位置)
let x=e.offsetX
let y=e.offsetY
let pixel=con.getImageData(x,y,1,1)//获取鼠标点击位置的一像素对象,并存到pixel变量
//设置显示的位置
clickDiv.style.background=`rgba(${pixel.data[0]}, ${pixel.data[1]}, ${pixel.data[2]}, 1)`
clickDiv.textContent=`rgba(${pixel.data[0]}, ${pixel.data[1]}, ${pixel.data[2]}, 1)`
})
写入像素数据
使用putImageData() 进行写入
ctx.putImageData(myImageData, dx, dy);
dx 和 dy 参数表示你希望在场景内左上角绘制的像素数据所得到的设备坐标。
//创建自己的像素对象
let myImageData=con.createImageData(0,0,100,100)
//将创建的像素对象填充到画布
con.putImageData(myImageData,100,100)
ctx.putImageData(imagedata,
dx, dy, //图片相对原点位置
X, Y, //裁剪起始坐标
W, H//裁剪大小
);
//图片填充
img.onload=()=>{
ctx.drawImage(img,0,0,800,500)
//用像素提取图像
let myImageData=ctx.getImageData(0,0,800,500)
//将提取到的图像放到画布
ctx.putImageData(myImageData,0,500,400,250,100,100)
}
图像灰度处理
灰度算法 const lm =0.299r + 0.587g+ 0.114b;
//图像的灰度处理
//获取像素对象
let imageDt = con.getImageData(0, 0, 800, 500);
for (let i = 0; i < imageDt.data.length; i += 4) {
//解构赋值,将每个rgb值赋值到常量中
let [r, g, b] = [
imageDt.data[i],
imageDt.data[i + 1],
imageDt.data[i + 2],
];
//灰度处理
let lm = r * 0.299 + g * 0.587 + b * 0.114;
//再将灰度的rgb传回去
imageDt.data[i] = lm;
imageDt.data[i + 1] = lm;
imageDt.data[i + 2] = lm;
}
con.putImageData(imageDt, 0, 500, 0, 0, 800, 500);
图像马赛克处理
let [width, height] = [canvas.width, canvas.height];
console.log(width, height);
const size = 20; //马赛克色块尺寸
let img = new Image();
img.src = "../../../src/命脉.png";
img.onload = () => {
ctx.drawImage(img, 0, 0, 800, 500);
let imageDt = ctx.getImageData(0, 0, 800, 500);
//行列遍历像素
for (let y = 0; y < 500; y += size) {
for (let x = 0; x < 800; x += size) {
const i = (y * 800 + x) * 4;
const [r, g, b] = [
imageDt.data[i],
imageDt.data[i + 1],
imageDt.data[i + 2],
];
ctx.fillStyle = `RGB(${r},${g},${b})`;
ctx.fillRect(x, y, size, size);
}
}
};
合成和剪裁
透明度
ctx.globalAlpha=value
值在0-1 之间,设置透明度
ctx.fillStyle = "#000";
ctx.globalAlpha = 0.2;
for (i = 0; i < 7; i++) {
ctx.beginPath();
ctx.arc(250, 250, 10 + 10 * i, 0, Math.PI * 2, true);
ctx.fill();
}
ctx.restore();
全局合成
全局合成指已绘图像(source)和将绘图像(destination)的合成方式
ctx.globalCompositeOperation=type
/* type值
* source-atop 新图形只在与现有画布内容重叠的地方绘制
* source-in 新图形只在新图形和目标画布重叠的地方绘制。其他的都是透明的。
* source-out 在不与现有画布内容重叠的地方绘制新图形。
* source-over 默认 这是默认设置,并在现有画布上下文之上绘制新图形。
* destination-atop 现有的画布只保留与新图形重叠的部分,新的图形是在画布内容后面绘制的
* destination-in 现有的画布内容保持在新图形和现有画布内容重叠的位置。其他的都是透明的
* destination-out 现有内容保持在新图形不重叠的地方。
* destination-over 在现有的画布内容后面绘制新的图形。
* lighter 两个重叠图形的颜色是通过颜色值相加来确定的。
* copy 只显示新图形。
* xor 图像中,那些重叠和正常绘制之外的其他地方是透明的
* ...
* */
| source | destination |
---|
atop | 已绘图像顶部显示将绘图像 | 将绘图像顶部显示已绘图像 | in | 已绘图像内部显示将绘图像 | 将绘图像内部显示已绘图像 | out | 已绘图像外部显示~ | 将绘图像内部显示~ | over | 将绘图像覆盖已绘图像显示 | 已绘图像覆盖将绘图像显示 |
ctx.fillStyle='#f0f'
ctx.fillRect(0,0,300,300)
ctx.beginPath()
ctx.globalAlpha=0.7
ctx.fillStyle = 'green';
// ctx.globalCompositeOperation="source-atop"
// ctx.globalCompositeOperation="source-in"
// ctx.globalCompositeOperation="source-out"
// ctx.globalCompositeOperation="source-over"
// ctx.globalCompositeOperation="destination-atop"
// ctx.globalCompositeOperation="destination-in"
// ctx.globalCompositeOperation="destination-out"
// ctx.globalCompositeOperation="destination-over"
// ctx.globalCompositeOperation="xor"
// ctx.globalCompositeOperation="lighter"
// ctx.globalCompositeOperation="copy"
ctx.arc(300,300,100,0,Math.PI*2)
ctx.fill()
clip() 裁剪
clip() 作用是将当前图形作为一个画布。画在上面的图形,其突出的部分不会显示。
ctx.fillStyle='blue'
ctx.arc(300,300,200,0,Math.PI*2,true)
ctx.fill()
ctx.clip()
ctx.fillStyle='red'
ctx.fillRect(300,300,200,200)
运动动画
动画
使用setTimeout ,setInterval ,RequestAnimationFrame 来进行动画创作
setTimeOut(fn,time) 和setInterval(fn,time)
优点:使用方便,动画的时间间隔可以自定义。
缺点:隐藏浏览器标签后,会依旧运行,造成资源浪费。与浏览器刷新频率不同步。
requestAnimationFrame(fn)
优点:性能更优良。隐藏浏览器标签后,便不会运行。与浏览器刷新频率同步。
缺点:动画的时间间隔无法自定义
//小球和画布的创建
//创建一个画布的长宽
const [width, height] = [canvas.width, canvas.height];
class ball {
//构造函数,构造小球
constructor(x, y, r, color, vx, vy) {
this.x = x;
this.y = y;
this.r = r;
this.color = color;
this.vx = vx;
this.vy = vy;
}
render(ctx) {
//每次渲染之前清空画布
ctx.clearRect(0, 0, width, height);
ctx.save();
//画布中线创建
ctx.beginPath();
ctx.moveTo(width / 2, 0);
ctx.lineTo(width / 2, height);
ctx.closePath();
ctx.stroke();
//碰撞线创建
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, height);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(width, 0);
ctx.lineTo(width, height);
ctx.closePath();
ctx.stroke();
//小球创建
ctx.beginPath();
ctx.globalAlpha = 0.5;
ctx.fillStyle = `color`;
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, true);
ctx.fill();
}
}
const flexBall = new ball(30, 30, 30, "black", 30, 30);
flexBall.render(ctx);
动画的创建
//使用setInterval()
// setInterval(()=>{
// //小球做往复运动
// if(flexBall.x+flexBall.r>=width||(flexBall.vx<0&&flexBall.x-flexBall.r<=0)){
// flexBall.vx=flexBall.vx*(-1)
// }
// flexBall.x+=flexBall.vx
// flexBall.render(ctx)
// },100)
//使用动画函数
animationFun();
function animationFun() {
if (
flexBall.x + flexBall.r >= width ||
(flexBall.vx < 0 && flexBall.x - flexBall.r <= 0)
) {
flexBall.vx = flexBall.vx * -1;
}
flexBall.x += flexBall.vx;
flexBall.render(ctx);
requestAnimationFrame(animationFun);
}
|