资讯专栏INFORMATION COLUMN

Canvas

spademan / 1864人阅读

摘要:对于平行线来说,仿射变换也会保持平行。仿射变换用个参数描述成为如下表述通过传入参数实现仿射变换对于坐标变换来说,除非进行刷新,否则,已经绘制的图形,不会进行消失,所有的变换,都不能对已经绘制的图形进行更改。

介绍

SVG是构建XML树的方式来达到绘制图形的,canvas是通过调用相关的方法来绘制图形的。
区别:SVG绘制图形,通过移除或者更改DOM方式来而使用canvas需要把图片从新擦除。
绘制的API在绘制上下文中定义。而不在画布中定义。
需要获得上下文对象的时候,需要调用画布的getContext方法,获得绘画的上下文。

画布元素和上下文,属于两个不同的对象,其中画布元素为canvas画布,而上下文对象为绘制需要的上下文。

关于3D图形,即,webGL 为封装了基本的OPENGL,当调用webGL的时候,其浏览器会调用OpenGL相关的API

绘制圆



    
    Title


第一个园
第二个园
// 获取画布元素
let canvas = document.getElementById("square");
// 获取绘制2D元素上下文
let context = canvas.getContext("2d");
// 设置填充颜色为红色
context.fillStyle = "#f00";
// 填充一个正方形
context.fillRect(10,0,10,10);
绘制线段,填充多边形
// 获取画布元素
let canvas = document.getElementById("square");
// 获取绘制2D元素上下文
let context = canvas.getContext("2d");
// 开始一条路径
context.beginPath();
// 从100,100 开始定义一条新的子路径
context.moveTo(100,100);
// 从100 100 到 200 200 绘制一条线段
context.lineTo(200,200);
// 从200 200 到 100 200 绘制一条线段
context.lineTo(100,200);
// 从100 200 到 100 100 绘制一条路径
context.lineTo(100,100);
// 绘制边
context.stroke();
// 进行填充
context.fill();
绘制多边形

以五边形为例子,

var canvas = document.getElementById("square");
var context = canvas.getContext("2d");
// 绘制一个以100,100为中心,半径为20的柜子N变形,每个定点均匀分布在圆角上,第一个定点放置在最上下
// 偏转角度为0
// 开始定义一条子路径
context.moveTo(100 + 20 * Math.sin(0), 100 - 20 * Math.cos(0));
// 计算两个顶点之间夹角
// 其中2π为一个园,除以边数,得到需要旋转的角度
var delta = 2 * Math.PI/5;
console.log(delta);
// 循环剩余每个顶点
var angle = 0;
for(var i = 1; i < 5; i++){
    //  角度累加
    angle += delta;
    // 通过旋转绘制下一个顶点,不断的旋转绘制
    context.lineTo(100 + 20 * Math.sin(angle), 100 - 20*Math.cos(angle));
}
// 最后一个顶点和起点进行连接
context.closePath();
// 从新开始一条新路径
context.stroke();
context.fill();
同理,画圆
var canvas = document.getElementById("square");
var context = canvas.getContext("2d");
// 绘制一个以100,100为中心,半径为20的柜子N变形,每个定点均匀分布在圆角上,第一个定点放置在最上下
// 偏转角度为0
// 开始定义一条子路径
context.moveTo(100 + 20 * Math.sin(0), 100 - 20 * Math.cos(0));
// 计算两个顶点之间夹角
// 其中2π为一个园,除以边数,得到需要旋转的角度
var delta = 2 * Math.PI/500000;
console.log(delta);
// 循环剩余每个顶点
var angle = 0;
for(var i = 1; i < 500000; i++){
    //  角度累加
    angle += delta;
    // 通过旋转绘制下一个顶点,不断的旋转绘制
    context.lineTo(100 + 20 * Math.sin(angle), 100 - 20*Math.cos(angle));
}
// 最后一个顶点和起点进行连接
context.closePath();
// 从新开始一条新路径
context.stroke();
context.fill();
非零绕数原则

要检测一个点p是否在路径内部,使用非零绕数原则,即,一条从点p出发沿着任意方向无限延伸,或者一直延伸到路径所在的区域外某点的射线,现在从0开始初始化一个计数器,对穿过这条射线的路径进行枚举,每当一条路径顺时针方向穿过射线的时候,计数器加1,逆时针减1,最后,枚举完所有路径以后,如果计时器的值不是0,那么就认为p在路径内,反过来,计数器的值为0,p在路径外。

js根据非零绕数原则确定那个在路径内,那个在路径外,用于进行填充。

图形属性

可以通过设置画布上下文的fillStyle等属性,设置图形的属性,例如对画布上下文的fillStyle的属性进行设置,即,可以设置出填充时的颜色,渐变,图案等样式。

对于canvas来说,每次获取上下文对象的时候,都会返回同一个上下文对象,即,上下文对象为单例的。

还可以使用save方法,把当前的状态,压入已经保存的栈中,调用restore方法,把状态进行恢复,即弹栈。

画布尺寸坐标

画布的默认的坐标系为左上角的坐标原点(0,0),右边数值大,下数值大,使用浮点数指定坐标,但不会自动转换为整数,会用反锯齿的方式,模拟填充部分元素。

画布尺寸不能随意改变,对任意属性进行操作,都会清空整个画布。

坐标系变换

每一个点的坐标都会映射到css像素上,css像素会映射到一个或多个设备像素。
画布中的特定操作,属性使用默认坐标系。
画布还有当前变换矩阵。
画布还有当前变换矩阵,当前变换矩阵作为图形状态的一部分。矩阵定义了当前画布的坐标系。
画布的操作会把该点映射到当前的坐标系中。

坐标变换

当调用c.translate(dx,dy)方法的时候,会进行如下变换

translate会进行坐标的上下移动
x" = x + dy;
y" = y + dy;

缩放
如要进行缩放,进行的是如下的变换

x" = sx * x;
y" = sy * y;

进行旋转操作,进行的是如下变换

x" = x * cos(a) - y * sin(a);
y" = y * cos(a) - x * sin(a);

如果要先变换再伸缩,进行如下变换
需要先把现有坐标系映射成为坐标系中的点x’, y" 然后再变换到x‘’ , y‘’

x"" = sx*x + dx;
y"" = sy*y + dy;

如果变换顺序相反进行如下变换

x"" = sx*(x + dx);
y"" = sy*(y + dy);

这种变换称为仿射变换,并且仿射变换会修改点的距离和线段间的夹角。对于平行线来说,仿射变换也会保持平行。仿射变换用6个参数描述成为如下表述

x" = ax + cy + e;
y" = bx + dy + f;

通过传入参数实现仿射变换

对于坐标变换来说,除非进行刷新,否则,已经绘制的图形,不会进行消失,所有的变换,都不能对已经绘制的图形进行更改。栗子如下
var canvas = document.getElementById("square");
var context = canvas.getContext("2d");
// 通过坐标变换实现科赫雪花
// 开始一条路径
context.beginPath();
// 开始绘制子路径
context.moveTo(100,100);
// 继续绘制
context.lineTo(200,200);
// 继续绘制
context.lineTo(200,200);
// 进行绘制边
context.stroke();
context.translate(200,200);
// 开始一条路径
context.beginPath();
// 开始绘制子路径
context.moveTo(100,100);
// 继续绘制
context.lineTo(200,200);
// 继续绘制
context.lineTo(200,200);
// 进行绘制边
context.stroke();

已经绘制的图形不会进行改变,改变的是已经绘制的图形

科赫雪花
var canvas = document.getElementById("square");
var context = canvas.getContext("2d");
// 通过坐标变换实现科赫雪花
// 当前状态入栈
function leg(n) {
    // 保存状态
    context.save();
    // 递归画
    if(n == 0){
        context.lineTo(50, 0);
    }else{
        // 定义为v字型
        context.scale(1/2,1/2);
        // 递归第一条
        context.rotate(60 * (Math.PI / 180));
        leg(n - 1);
        context.rotate(-120 * (Math.PI / 180));
        leg(n - 1);
    }
    // 坐标恢复变换
    context.restore();
    // 恢复下一个坐标为0,0
    context.translate(50, 0);
}

context.save();
context.moveTo(50, 50);
// 绘制第一条
leg(1);
context.stroke();
绘制填充曲线

绘制一些常见的图形

var canvas = document.getElementById("square");
var context = canvas.getContext("2d");
// 工具函数,角度转弧度
function rads(x) {
    return Math.PI * x / 180;
}

// 绘制园
context.beginPath();
context.arc(100,100,40, 0, rads(360), false);
context.stroke();
context.fill();

同理绘制贝塞尔曲线也是同理。

颜色,透明度,渐变,图案

绘制一个渐变
需要使用createLinearGradient获取一个进行渐变的上下文,对这个上下文进行处理。然后其颜色设置为这个渐变的上下文,即,fillStyle属性。

线段绘制 封顶

对于线段,有三种封顶方式,即,butt,square,round
在绘制图形以后,会参数尖角,圆角,平角,三种。
lineCap属性

文本

和css类似,基线问题。

裁剪

直接调动clip即可,当前路径也会被裁剪进入,路径外的统统不会显示。

阴影

设置shadow属性即可

图片

画布API支持位图图片,同时也支持canvas导出成为图片。

// 创建一个img元素
let img = document.createElement("img");
// 设置src属性
img.src = canvas.toDataURL();
// 追加到文档后面
document.body.appendChild(img);
合成

一些api不在阐述

像素操作

调用getImageDate方法返回ImageDate对象
使用createImageDate()可以创建像素容器
进行动态模糊先获取像素的ImageDate对象,然后再获取该对象的data属性,该data为一个数组。为一个维数组。每四个元素代表红色分量,绿色分量,蓝色分量,透明度分量。(Alpha分量)
其色素直为0-1,即,数组元素中保存的数组为色素值。
每四个每四个元素遍历。然后把其色素值的1/ n + 上一个色块的m/n 然后赋值给新的色块,代码如下

// row为行数
for(var row = 0; row < height; row++){
    // 获得每行第二个元素的偏移量,其中width为行的色素块。
    var i = row * width * 4;
    // 每4个的色素值进行处理
    for(var col = 1; col < width; col++, i+=4){
        // 对红色分量处理
        data[i] = (data[i] + data[i - 4] * m) / n;
        // 对绿色分量处理
        data[i + 1] = (data[i + 1] + data[i + 1 - 4] * m) / n;
        // 对蓝色分量处理
        data[i + 2] = (data[i + 2] + data[i + 2 - 4] * m) / n;
        // 对透明度分量处理
        data[i + 3] = (data[i + 3] + data[i + 3 - 4] * m) / n;
    }
}

然后把其色素块进行复制回去即可。
其中每个像素占据一个字节,一个四个字节。

命中检测

isPointInPath方法用来确定一个点是否落在当前路径中。
即命中检测。

命中检测可以和鼠标事件相互转化

但是坐标需要进行转换。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101601.html

相关文章

  • [译][Tkinter 教程08] Canvas 图形绘制

    摘要:已获原作者授权原系列地址简介为提供了绘图功能其提供的图形组件包括线形圆形图片甚至其他控件控件为绘制图形图表编辑图形自定义控件提供了可能在第一个例子里我们将演示如何画一条直线方法用来绘制一条直线为以整形表示的四个坐标参数这表示所要绘制的直线连 已获原作者授权. 原系列地址: Python Tkinter 简介 Canvas 为 Tkinter 提供了绘图功能. 其提供的图形组件包括 线...

    ivydom 评论0 收藏0
  • 网页保存为图片及高清截图的优化 | canvas跨域图片配置

    摘要:本次技术调研来源于项目中的一个重要功能需求实现微信长按网页保存为截图。以下主要解决两类跨域的图片资源包括已配置过的中的图片资源和微信用户头像图片资源。 本次技术调研来源于H5项目中的一个重要功能需求:实现微信长按网页保存为截图。 这里有个栗子(请用微信打开,长按图片即可保存):3分钟探索你的知识边界 将整个网页保存为图片是一个十分有趣的功能,常见于H5活动页的结尾页分享。以下则是项目中...

    zhaochunqi 评论0 收藏0
  • 网页保存为图片及高清截图的优化 | canvas跨域图片配置

    摘要:本次技术调研来源于项目中的一个重要功能需求实现微信长按网页保存为截图。以下主要解决两类跨域的图片资源包括已配置过的中的图片资源和微信用户头像图片资源。 本次技术调研来源于H5项目中的一个重要功能需求:实现微信长按网页保存为截图。 这里有个栗子(请用微信打开,长按图片即可保存):3分钟探索你的知识边界 将整个网页保存为图片是一个十分有趣的功能,常见于H5活动页的结尾页分享。以下则是项目中...

    GHOST_349178 评论0 收藏0
  • 网页保存为图片及高清截图的优化 | canvas跨域图片配置

    摘要:本次技术调研来源于项目中的一个重要功能需求实现微信长按网页保存为截图。以下主要解决两类跨域的图片资源包括已配置过的中的图片资源和微信用户头像图片资源。 本次技术调研来源于H5项目中的一个重要功能需求:实现微信长按网页保存为截图。 这里有个栗子(请用微信打开,长按图片即可保存):3分钟探索你的知识边界 将整个网页保存为图片是一个十分有趣的功能,常见于H5活动页的结尾页分享。以下则是项目中...

    Galence 评论0 收藏0
  • canvas.width和canvas.style.width区别以及应用

    摘要:今天讲的内容是和的区别,在没有做项目之前,其实我是并没有深入了解过这两个属性的,最近在研究项目的自适应问题,尤其是在中置入图片,碰到了图片模糊的问题,这些杂症都是和文章要讲的有关系。最后,当我们不指定的真实大小时,默认按照处理。 今天讲的内容是canvas.width和canvas.style.width的区别,在没有做canvas项目之前,其实我是并没有深入了解过这两个属性的,最近在...

    付永刚 评论0 收藏0
  • 基于html2canvas实现网页保存为图片及图片清晰度优化

    摘要:本次技术调研来源于项目中的一个重要功能需求实现微信长按网页保存为截图。小结是目前实现网页保存为图片功能的综合最佳选择。 本次技术调研来源于H5项目中的一个重要功能需求:实现微信长按网页保存为截图。 这里有个栗子(请用微信打开,长按图片即可保存):3分钟探索你的知识边界 将整个网页保存为图片是一个十分有趣的功能,常见于H5活动页的结尾页分享。以下则是项目中调研和踩坑的一些小结和汇总。 ...

    JerryWangSAP 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<