Shinkai005
V1
2022/04/21阅读:25主题:红绯
我要开始学canvas了
我要开始学canvas了
-
模板就是这玩意
function draw(){
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
if(canvas.getContext){
const ctx = canvas.getContext("2d");
ctx.fillStyle="rgb(200,0,0)";
ctx.fillRect(10,10,350,300);
ctx.fillStyle = "rgba(0,0,200,0.5)";
ctx.fillRect(30,30,350,300);
}
}
<canvas id="canvas" width="800" height="600">
你这浏览器不支持这玩意啊
</canvas>
css
body{
text-align: center;
padding-top: 20px;
}
canvas{
box-shadow: 0 0 10px #333;
margin: 0 auto;
border-radius: 25px;
}
描边矩形
// 描边矩形
ctx.strokeStyle = "rgb(200,0,0)";
ctx.strokeRect(10,10,350,300);
ctx.strokeStyle = "rgba(0,0,200,0.5)";
ctx.strokeRect(30,30,350,300);
填充矩形
// 填充矩形
ctx.fillStyle="rgb(200,0,0)";
ctx.fillRect(100,100,350,300);
ctx.fillStyle = "rgba(0,0,200,0.5)";
ctx.fillRect(250,250,350,300);
图形 (三角形)
function draw(){
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
if(canvas.getContext){
const ctx = canvas.getContext("2d");
/** 图形的基本元素就是路径
*
* 1.首先,创建起始路径 beginPath();
* 2. 使用画图命令画出路径 moveTo()
* 3. 把路径闭合
* 4. 通过描边或者填充来绘制.
*/
/*绘制一个三角形*/
ctx.beginPath();
// 起始点
ctx.moveTo(50,500);
// 第一条线 连接两点
ctx.lineTo(400,250);
// 第二条线
ctx.lineTo(50,0);
// 第三条线
// ctx.lineTo(50,500);
// 可以不使用第三条线,用closePath()闭合图形
ctx.closePath();
// 填充三角形
// ctx.fill();
// 描线三角形
ctx.stroke();
}
}
作业一个骚骚的图形

function draw(){
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
if(canvas.getContext){
const ctx = canvas.getContext("2d");
/** 图形的基本元素就是路径
*
* 1.首先,创建起始路径 beginPath();
* 2. 使用画图命令画出路径 moveTo()
* 3. 把路径闭合
* 4. 通过描边或者填充来绘制.
*/
/* 绘制一个矩形 */
ctx.strokeRect(50,50,500,500)
/* 绘制左上方三角形 */
// 开始绘制路径
ctx.beginPath();
// 起始点
ctx.moveTo(150,150);
// 第一条线
ctx.lineTo(400,150);
// 第二条线
ctx.lineTo(150,400);
// 闭合
ctx.closePath();
// 绘制
ctx.fill();
/* 绘制第二个三角形 */
ctx.beginPath();
ctx.moveTo(450,450);
ctx.lineTo(200,450);
ctx.lineTo(450,200);
ctx.closePath();
ctx.stroke();
/* 绘制中间十字 */
// 第一条线
ctx.beginPath();
ctx.moveTo(300,275);
ctx.lineTo(300,325);
ctx.stroke();
// 第二条线
ctx.beginPath();
ctx.moveTo(275,300);
ctx.lineTo(325,300);
ctx.stroke();
}
画圆
function draw(){
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
if(canvas.getContext){
const ctx = canvas.getContext("2d");
// 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
ctx.strokeStyle ='orange';
ctx.lineWidth = 10;
// 画圆
//ctx.arc(x,y,radius,startAngle,endAngle,anticlockwise);
//ctx.arc(400,300,150,0,Math.PI/2,false);
ctx.arc(400,300,150,0,Math.PI*3/2,true);
ctx.stroke();
}
}
循环画圆
function draw(){
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
if(canvas.getContext){
const ctx = canvas.getContext("2d");
// 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
/*循环生成圆弧*/
let x=150;
let y=60;
const r=50;
let end = Math.PI;
let anticlockWise=false;
ctx.lineWidth="2";
const rule1 = [0,1];
const rule2 = [2,3];
for (let i = 0; i < 4; i++) {
i!==0&&(x=150,y+=150,end=Math.PI,anticlockWise =!anticlockWise);
for (let j = 0; j < 3; j++) {
ctx.beginPath();
ctx.arc(x,y,r,0,end,anticlockWise);
rule1.includes(i)&&ctx.stroke();
rule2.includes(i)&&ctx.fill();
x+=250;
end+=1/2*Math.PI;
}
}
}
}
优化一下~
“includes实在是为了炫技而炫技..
”
function draw(){
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
if(canvas.getContext){
const ctx = canvas.getContext("2d");
// 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
/*循环生成圆弧*/
let x=150;
let y=60;
const r=50;
let end = Math.PI;
let anticlockWise=false;
ctx.lineWidth="2";
const rule1 = [0,1];
const rule2 = [2,3];
for (let i = 0; i < 4; i++) {
i!==0&&(x=150,y+=150,end=Math.PI,anticlockWise =!anticlockWise);
for (let j = 0; j < 3; j++) {
ctx.beginPath();
ctx.arc(x,y,r,0,end,anticlockWise);
(i<2)&&ctx.stroke();
(i>=2)&&ctx.fill();
x+=250;
end+=1/2*Math.PI;
}
}
}
}
“和老师写的不一样,也没啥谁更优的,当然是我了!人要自信
老师在循环里声明变量,其实理论上会消耗性能一些,但是如今的V8太先进了可以忽略不计.
”
画笑脸
function draw(){
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
if(canvas.getContext){
const ctx = canvas.getContext("2d");
// 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
/* 外部圆 */
ctx.beginPath();
ctx.arc(400,300,300,0,Math.PI*2,false);
ctx.stroke();
/* 两个眼睛 */
ctx.beginPath();
ctx.arc(250,200,25,0,Math.PI*2,false);
ctx.stroke();
ctx.beginPath();
ctx.arc(550,200,25,0,Math.PI*2,false);
ctx.stroke();
/* 嘴巴 */
ctx.beginPath();
ctx.arc(400,300,200,Math.PI/4,Math.PI*3/4,false);
ctx.stroke();
}
}
优化:
“一个图,分成几个部分画不太好~可以采用重绘起点的方式来让他们之间断开连接.
”
function draw(){
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById("canvas");
if(canvas.getContext){
const ctx = canvas.getContext("2d");
// 圆的组成: 圆心(x,y) 半径 开始角度 结束角度 顺时针/逆时针
/* 外部圆 */
ctx.beginPath();
ctx.arc(400,300,300,0,Math.PI*2,false);
/* 两个眼睛 */
ctx.moveTo(275,200);
ctx.arc(250,200,25,0,Math.PI*2,false);
ctx.moveTo(575,200);
ctx.arc(550,200,25,0,Math.PI*2,false);
ctx.stroke();
/* 嘴巴 */
ctx.beginPath()
ctx.arc(400,300,200,Math.PI/4,Math.PI*3/4,false);
ctx.stroke();
}
}
嘴巴位置起点位置不好找我就重建路径了.
线性渐变linear gredient
用的不多~
function draw() {
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
/**
* 线性渐变
* 参数1: 起点x1
* 参数2: 起点y1
* 参数3: 起点x2
* 参数4: 起点y2
*
*/
const linearGradient = ctx.createLinearGradient(0, 0, 0, 150)
/**
* linearGradient.addColorStop(0,'#cc6677');
* 参数1: 必须是0.0~1.0之间的一个数值,数值表示颜色所在的相对位置
* 参数2: 颜色,
*/
linearGradient.addColorStop(0, '#cc6677')
linearGradient.addColorStop(0.5, '#fff')
linearGradient.addColorStop(0.5, '#c6c776')
const linearGradient2 = ctx.createLinearGradient(0, 50, 0, 90)
linearGradient2.addColorStop(0.5, '#000')
linearGradient2.addColorStop(1, 'rgba(0,0,0,0)')
ctx.fillStyle = linearGradient
ctx.fillRect(10, 10, 150, 150)
ctx.strokeStyle = linearGradient2
ctx.strokeRect(60,50,50,50)
}
}
Radial gradient 辐射渐变
function draw() {
// 让vscode可以显示canvas智能提示
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
/**
* 径向渐变 radial gradient
* 参数1: x1
* 参数2: y1
* 参数3: r1
* 参数4: x2
* 参数5: y2
* 参数6: r2
*/
const radialGradient = ctx.createRadialGradient(170,170,10,180,180,30);
radialGradient.addColorStop(0,'#a7d30c');
radialGradient.addColorStop(0.9,'#019f62');
radialGradient.addColorStop(1,'rgba(1,159,98,0)');
const radialGradient2 = ctx.createRadialGradient(280,290,10,280,280,80)
radialGradient2.addColorStop(0,'#ff5f98');
radialGradient2.addColorStop(0.9,'#ff0188');
radialGradient2.addColorStop(1,'rgba(255,1,136,0)');
//画图
ctx.fillStyle = radialGradient;
ctx.fillRect(0,0,800,800);
ctx.fillStyle = radialGradient2;
ctx.fillRect(0,0,325,325);
}
}
绘制图片
绘制图片有个问题,一定要先让图片加载出来再渲染.
我这块用了promise.
function draw() {
// 让vscode可以显示canvas智能提示
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
// 用promise对象判断是否加载完毕
const loadImage = function (src) {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = src
aim = img
aim.onload = () => {
resolve(aim)
}
aim.onerror = (e) => {
reject(e)
}
})
}
// loadResult是promise对象
const loadResult = loadImage('hello.png')
// .then是成功情况,.catch是失败会报错在控制台.
loadResult
.then((res) => {
const pattern = ctx.createPattern(res,"no-repeat")
ctx.fillStyle = pattern
ctx.fillRect(0, 0, 1000, 1000)
})
.catch((e) => {
console.log('error', e)
})
}
}
绘制文字阴影
function draw() {
// 让vscode可以显示canvas智能提示
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
//设置线性渐变linear gradient
const linerGradient = ctx.createLinearGradient(100,200,500,200);
linerGradient.addColorStop(0.5,'#cc6677');
linerGradient.addColorStop(1,'#000');
//设置阴影
ctx.shadowColor = 'orange'
ctx.shadowBlur = 10
ctx.shadowOffsetX = 20
ctx.shadowOffsetY = 20
/**
* font-weight
* font-style
* font-size
* font-family
*/
ctx.font = 'bold italic 160px arial'
ctx.fillStyle = linerGradient;
ctx.fillText('Shinkai', 100, 200)
}
}
drawImage
不想写了
function draw() {
// 让vscode可以显示canvas智能提示
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
const img = new Image()
img.src = 'hello.png'
img.onload = function () {
/**
* 1.图像
* 2.x轴
* 3.y轴
* 4.宽度
* 5.高度
* 6.绘制x轴
* 7.绘制y轴
* 8.绘制的宽度
* 9.绘制的高度
*/
// ctx.drawImage(this,0,0); //正常
// ctx.drawImage(this,0,0,300,200); //缩放
ctx.drawImage(this, 0, 0, 120, 100, 150, 50, 100, 50) //裁剪
}
}
}
制作房产证
function draw() {
// 让vscode可以显示canvas智能提示
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
const loadImage = function (src) {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = src
img.onload = () => {
resolve(img)
}
img.onerror = (e) => {
reject(e)
}
})
}
const loadResult = loadImage('fangchanzheng.jpg')
loadResult
.then((res) => {
// const pattern = ctx.createPattern(res, 'no-repeat')
// ctx.fillStyle = pattern
// ctx.fillRect(0, 0, 800, 600)
ctx.drawImage(res,0,0);
ctx.font = '10 Arial';
ctx.strokeStyle='rgb(131,121,118)'
ctx.strokeText('Shinkai',178,120);
ctx.stroke();
})
.catch((e) => {
console.log(e)
})
}
}
clip
在画布中呈现想要的形状带图案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Vue.js</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="style.css" />
<script src="vue.js"></script>
</head>
<body onload="draw()">
<div id="vue-app"></div>
<canvas id="canvas" width="800" height="600">
你这浏览器不支持这玩意啊
</canvas>
<img src="fitness.jpg" alt="">
<script src="app.js"></script>
<script>
function draw() {
// 让vscode可以显示canvas智能提示
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
const img = new Image();
img.src = "fitness.jpg";
img.onload = function(){
ctx.arc(400,300,150,0,Math.PI*2)
ctx.fill();//这句话图像不显示的时候显示的黑色
ctx.clip();
context.drawImage(this,0,0);
}
}
}
</script>
</body>
</html>
第二天
涂鸦
function draw() {
// 让vscode可以显示canvas智能提示
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
/* 涂鸦 */
// 添加鼠标的按下event
canvas.onmousedown = function (e) {
console.log('执行')
const ev = e || window.event //兼容
// xy 是 在画布上的坐标. 就是屏幕上当前的坐标-画布离左上角的坐标.
const x = ev.clientX - canvas.offsetLeft
const y = ev.clientY - canvas.offsetTop
console.log(x, y)
// 配置画笔
ctx.strokeStyle = 'green'
ctx.lineWidth = 10
//
ctx.beginPath()
ctx.moveTo(x, y)
// 因为不是只需要线,而是涂鸦所以还需要检测移动
canvas.onmousemove = function (e) {
console.log('移动')
const ev = e || window.event //兼容
// xy 是 在画布上的坐标. 就是屏幕上当前的坐标-画布离左上角的坐标.
const x = ev.clientX - canvas.offsetLeft
const y = ev.clientY - canvas.offsetTop
ctx.lineTo(x, y)
ctx.stroke()
}
// 设置结束事件也就是 mouseup
canvas.onmouseup = function (e) {
canvas.onmousemove = ''
}
}
}
}
优化要成为习惯~
“励志刷完b站所有课的男人+打死不用老师源码的人
”
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Vue.js</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- <link rel="stylesheet" href="style.css" /> -->
<style>
body {
text-align: center;
padding-top: 20px;
/* margin: 0; */
}
canvas {
box-shadow: 0 0 10px #333;
margin: 0 auto;
border-radius: 25px;
/* position: absolute; */
/* left: 0; */
}
</style>
<script src="vue.js"></script>
</head>
<body>
<div id="vue-app">
<canvas id="canvas" width="800" height="600">
你这浏览器不支持这玩意啊
</canvas>
<button @click="mountedMethod()" :value="control">
{{control?'正在涂鸦':'已停用涂鸦'}}
</button>
</div>
<!-- <script src="app.js"></script> -->
<script>
new Vue({
el: '#vue-app',
data: {
control: false,
},
created() {},
methods: {
draw() {
// 让vscode可以显示canvas智能提示
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
/* 涂鸦 */
// 添加鼠标的按下event
canvas.onmousedown = function (e) {
console.log('执行')
const ev = e || window.event //兼容
// xy 是 在画布上的坐标. 就是屏幕上当前的坐标-画布离左上角的坐标.
const x = ev.clientX - canvas.offsetLeft
const y = ev.clientY - canvas.offsetTop
console.log(x, y)
// 配置画笔
ctx.strokeStyle = 'green'
ctx.lineWidth = 10
//
ctx.beginPath()
ctx.moveTo(x, y)
// 因为不是只需要线,而是涂鸦所以还需要检测移动
canvas.onmousemove = function (e) {
console.log('移动')
const ev = e || window.event //兼容
// xy 是 在画布上的坐标. 就是屏幕上当前的坐标-画布离左上角的坐标.
const x = ev.clientX - canvas.offsetLeft
const y = ev.clientY - canvas.offsetTop
ctx.lineTo(x, y)
ctx.stroke()
}
// 设置结束事件也就是 mouseup
canvas.onmouseup = function (e) {
canvas.onmousemove = ''
}
}
}
},
mountedMethod() {
this.control = !this.control
this.$nextTick(() => {
this.draw()
})
},
},
})
</script>
</body>
</html>
上面是蛇精病写法....希望大家不会有一天遇到这样的需求....
“主要的点就在于 dom渲染后在执行需要用this.$nextTick方法.
然后还有个bug是 开了控制台 绘画位置和鼠标不在一起了. 需要检测一下是否开了控制台,然后再重新写一个计算xy方式.懒得改了
”
蒙版实现刮刮乐
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Vue.js</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" href="style.css" />
<script src="vue.js"></script>
</head>
<body onload="draw()">
<div id="vue-app"></div>
<img src="beautiful.jpg" alt="" />
<canvas id="canvas" width="800" height="600">
你这浏览器不支持这玩意啊
</canvas>
<script src="app.js"></script>
<script>
function draw() {
// 让vscode可以显示canvas智能提示
/** @type {HTMLCanvasElement} */
const canvas = document.getElementById('canvas')
if (canvas.getContext) {
const ctx = canvas.getContext('2d')
//蒙版
ctx.beginPath()
ctx.fillStyle = 'rgba(100,100,100,0.99)'
ctx.fillRect(0, 0, 240, 360)
ctx.globalCompositeOperation = 'destination-out'
ctx.lineWidth = 20
ctx.lineCap = 'round'
canvas.onmousedown = function (e) {
const ev = e || window.event
const x = ev.clientX
const y = ev.clientY
ctx.moveTo(x, y)
canvas.onmousemove = function (e) {
const ev = e || window.event
const x = ev.clientX
const y = ev.clientY
ctx.lineTo(x, y)
ctx.stroke()
}
canvas.onmouseup = function (e) {
canvas.onmousemove = ''
}
}
}
}
</script>
</body>
</html>
作者介绍
Shinkai005
V1
公众号:深海笔记Shinkai