一、canvas简介
- canvas(“画布”)自身是HTML5提供的一种新标签, <canvas>标签自身只是图形容器,须要通过脚本 (通常是JavaScript)能够进行图形的绘制(canvas有多种绘制门路、矩形、圆形、字符以及增加图像的办法)。
- canvas目前次要能够利用于游戏、数据可视化、图片的操作等畛域。
二、canvas根底用法
1.canvas标签
canvas标签能够说是咱们绘制的容器,内容的绘制咱们须要依附canvas的context对象来实现,标签有两个自有的属性width和height,用来规定画布的宽高,<canvas> 标签反对 HTML 的全局属性,另外咱们能够在标签中嵌入后备内容,在不反对的设施上让用户理解以后内容。
<canvas id="myCanvas" width="800" height="800"> 您的浏览器不反对canvas,请更换浏览器。</canvas>
2.canvas的坐标系
在进行绘制操作前咱们还须要理解一下canvas的坐标
3.根本api
context:canvas的上下文、绘制环境,所有操作的api都是基于context,咱们须要通过js来操作context来进行绘制,能够联合正文进行学习
从上面的代码中咱们能够学习如何进行文字、线段、矩形、圆形等根本图形的绘制
const canvasDom = document.getElementById('myCanvas') // 获取画布元素 const ctx = canvasDom.getContext('2d') // 获取canvas对象 2D绘图的上下文 ctx.font = '38px Arial' // 文本相干设置 ctx.fillStyle = 'orange' // 填充色 ctx.fillText('一些根底图形', 320, 300) // 绘制填充的文本 ctx.fillRect(50, 198, 50, 104) // 绘制矩形 ctx.beginPath() // 绘制门路开始 ctx.lineWidth = 3 // 线条宽度 ctx.strokeStyle = 'red' // 填充色 ctx.moveTo(100, 200) // 绘制终点(x, y) ctx.lineTo(300, 300) // 绘制直线(x,y) ctx.lineTo(100, 300) ctx.closePath() // 绘制门路闭合 闭合门路会主动把完结的点和开始的线连在一起 ctx.stroke() // 绘制线段 ctx.beginPath() ctx.lineWidth = 16 ctx.strokeStyle = 'blue' ctx.fillStyle = 'green' // 绘制圆形 圆心坐标 半径长度 初始角度 完结角度 顺时针、逆时针绘制 // (x,y,r,sAngle,eAngle,counterclockwise) ctx.arc(220, 194, 50, 0, 1 * Math.PI, false) ctx.stroke() ctx.fill() // 是将闭合的门路的内容进行填充 ctx.beginPath() ctx.strokeStyle = 'green' ctx.fillStyle = 'blue' ctx.arc(220, 194, 50, 0, 1 * Math.PI, true) ctx.stroke() ctx.fill() ctx.beginPath() ctx.fillStyle = 'red' ctx.lineWidth = 2 ctx.rect(110, 240, 50, 50) // 绘制矩形 (x, y, width, height) ctx.stroke() ctx.fill()
运行成果如图:
最初咱们能够应用clearRect进行革除操作,能够实现涂鸦的橡皮擦也能够整个画面做清空操作
参数和绘制矩形一样 坐标以及革除的长宽(x, y, width, hegiht)
ctx.clearRect(110, 240, 20, 20) // 革除部分 ctx.clearRect(0, 0, 800, 800) // 初始点坐标 加容器宽高 革除全副
图片的绘制会在上面案例进行解说
三、canvas根底案例
1.图片的放大旋转
绘制图片须要应用drawImage办法
drawImage(img,sx,sy,swidth,sheight,x,y,width,height)
参数 | 阐明 | 是否必须 |
---|---|---|
img | 规定要应用的图像、画布或视频 | 是 |
sx | 开始剪切的 x 坐标地位 | 否 |
sy | 开始剪切的 y 坐标地位 | 否 |
swidth | 被剪切图像的宽度 | 否 |
sheight | 被剪切图像的高度 | 否 |
x | 在画布上搁置图像的 x 坐标地位 | 是 |
y | 在画布上搁置图像的 y 坐标地位 | 是 |
width | 要应用的图像的宽度 | 否 |
height | 要应用的图像的高度 | 否 |
上面咱们联合实例看一下
<body> <canvas id="myCanvas" width="400" height="400"> 您的浏览器不反对canvas,请更换浏览器 </canvas> <canvas id="myCanvas1" width="400" height="400"> 您的浏览器不反对canvas,请更换浏览器 </canvas> <br/> <button onclick="draw()">旋转 rotate</button> <script> const canvasDom = document.getElementById('myCanvas') // 获取画布元素 const ctx = canvasDom.getContext('2d') // 获取canvas对象 2D绘图的上下文 const canvasDom1 = document.getElementById('myCanvas1') // 获取画布元素 const ctx1 = canvasDom1.getContext('2d') // 获取canvas对象 2D绘图的上下文 // canvasDom.style.backgroundColor = 'orange' const imgBoxSize = 400 // 容器尺寸 let currentRotate = 0 // 以后旋转角度 function draw () { ctx.clearRect(0, 0, 400, 400) const img = new Image() img.src = './img.png' // img.crossOrigin = '*' // 开启CORS性能 // 图片加载 img.onload = function () { ctx.beginPath() // 旋转图片 if (currentRotate > 0) { ctx.save() // 保留以后的绘图状态 const xpos = imgBoxSize / 2 const ypos = imgBoxSize / 2 // canvas 中的所有几何变换针对的不是绘制的图形,而是针对画布自身 ctx.translate(xpos, ypos) // 调整画布初始点地位 ctx.rotate(currentRotate * Math.PI / 180) // 旋转以后的绘图 ctx.translate(-xpos, -ypos) // 还原 } // 绘制 ctx.drawImage(img, 0, 0, imgBoxSize, imgBoxSize) // 绘制放大 ctx1.drawImage(img, 120, 120, 50, 50, 0, 0, imgBoxSize, imgBoxSize) ctx.closePath() if (currentRotate > 0) { ctx.restore() // 复原之前保留的绘图状态 } currentRotate += 90 if (currentRotate > 270) { currentRotate = 0 } } } draw () </script></body>
2.本地压缩图片下载&&惯例文件格式体积限度
<body> <input type="file" id="upload" /> <script> const ACCEPT = ['image/jpg', 'image/png', 'image/jpeg'] // 容许的图片格式 const maxSize = 1024 * 1024 // 文件最大 const uploadDom = document.getElementById('upload') // 获取upload元素 const max = 1024 // 压缩图片尺寸边界 let fileType = '' // 保留文件类型 以及 名称 从新生成时进行回填 let fileName = '' // base64读取 执行压缩回调 function converImgToBase64 (file, cb) { let reader = new FileReader() reader.addEventListener('load', function (e) { const base64Img = e.target.result cb && cb(base64Img, downloadImg) reader = null }) reader.readAsDataURL(file) } // 压缩 function compress (base64Img, cb) { let imgW, imgH; const _img = new Image() _img.src = base64Img _img.addEventListener('load', function (e) { let ratio; // 图片长宽比 let needCompress = false // 是否须要压缩判断 imgW = _img.naturalWidth // 图像的原始宽度 imgH = _img.naturalHeight // 图像的原始高度 // 图片宽高任意超过咱们设置的最大边界 进行压缩 if (max < _img.naturalHeight || max < _img.naturalWidth) { needCompress = true if (_img.naturalHeight < _img.naturalWidth) { ratio = _img.naturalWidth / max } else { ratio = _img.naturalHeight / max } imgW = imgW / ratio imgH = imgH / ratio } // 创立canvas元素 渲染解决尺寸后的图片 const _canvas = document.createElement('canvas') _canvas.setAttribute('id', 'compressImg') _canvas.width = imgW _canvas.height = imgH // 暗藏元素 _canvas.style.visibility = 'hidden' document.body.appendChild(_canvas) const ctx = _canvas.getContext('2d') ctx.clearRect(0, 0, imgW, imgH) ctx.drawImage(_img, 0, 0, imgW, imgH) if (!fileName || !fileType) { return _canvas.remove() } // 办法返回一个蕴含图片展现的 data URI // (type, encoderOptions) 图片格式 图片品质0-1之间 const compressImg = _canvas.toDataURL(fileType, 0.8) cb && cb(compressImg) _canvas.remove() // 移除元素 }) } // 创立a标签触发点击事件进行下载 function downloadImg (file) { const _a = document.createElement('a') _a.href = file _a.setAttribute('download', fileName) // 应用读取文件保留的文件名 _a.click() } uploadDom.addEventListener('change', function (e) { const [file] = e.target.files if (!file) { return } const { type: _fileType, size: _fileSize, name: _fileName } = file // 文件类型查看 if (!ACCEPT.includes(_fileType)) { uploadDom.value = '' return console.error('不反对以后文件格式') } fileType = _fileType fileName = _fileName // 文件体积查看 if (_fileSize > maxSize) { uploadDom.value = '' return console.error('以后文件体积过大') } // base64转化 converImgToBase64(file, compress) }) </script></body>
3.解决canvas转图片不清晰
在开发中我已经遇到了绘制的图片看起来非常的含糊,实际上是画布尺寸与画布范畴内理论像素不统一造成。为了解决这个问题,优化用户体验我才用的计划是间接解决canvas转图片不清晰,间接容器宽高*2解决的
const canvas = document.getElementById('canvas') canvas.style.backgroundColor = '#FFF' canvas.style.width = _width + 'px' canvas.style.height = _height + 'px' canvas.width = _width * 2 canvas.height = _height * 2
更优的计划应该是依据设施的dpr去做
const canvas = document.getElementById('canvas')const dpr = (scale = window.devicePixelRatio || 1)const rect = canvas.getBoundingClientRect() canvas.width = rect.width * dpr canvas.height = rect.height * dpr canvas.style.width = rect.width + 'px' canvas.style.height = rect.height + 'px'
解决前
解决后
4.最初附上一个小成果
<body> <canvas id="myCanvas"> 您的浏览器不反对canvas,请更换浏览器 </canvas> <script> const canvasDom = document.getElementById('myCanvas') const ctx = canvasDom.getContext('2d') const getRandomFloat = (min, max) => (max - min) * Math.random() + min const getRandomInt = (min, max) => Math.floor(getRandomFloat(min, max + 1)) class Container { constructor() { this.update() } update() { this.width = window.innerWidth this.height = window.innerHeight this.center = { x: this.width / 2, y: this.height / 2 } canvasDom.width = this.width canvasDom.height = this.height } } const _container = new Container() const maxAmp = 250 const minAmp = 150 const numParticles = 120 class Tracks { constructor() { const numTracks = 20 this.tracks = [] for (let i = 0; i < numTracks; ++i) { const ratio = i / numTracks this.tracks.push({ amp: (maxAmp - minAmp) * ratio + minAmp }) } } } const { tracks } = new Tracks() class Particle { constructor() { this.init() } init() { this.baseAmp = tracks[getRandomInt(0, tracks.length - 1)].amp this.rotSpeed = getRandomFloat(0.01, 0.015) this.ampSpeed = 1 this.baseRad = 3 this.more = 10 this.num = 0 this.amp = this.baseAmp this.rad = this.baseRad this.angle = getRandomFloat(0, Math.PI * 2) this.pos = { x: 0, y: 0 } } updateRadius() { let ratio = this.amp / this.baseAmp * 2.5 - 1.5 this.rad = ratio * this.baseRad } updatePosition() { this.angle += this.rotSpeed this.pos.x = _container.center.x + this.amp * Math.cos(this.angle) * 1.2 this.pos.y = _container.center.y + this.amp * Math.pow(Math.sin(this.angle), 3) * 0.8 } draw() { const { pos } = this const ageAttack = this.num / this.more const rad = this.rad * ageAttack const alpha = ageAttack ctx.beginPath() ctx.arc(pos.x, pos.y, rad, 0, Math.PI * 2) ctx.fillStyle = `rgba(${Math.floor(Math.random()*255)}, 255, 255, ${alpha}` ctx.fill() } update() { if (this.num < this.more) { this.num++ } this.amp -= this.ampSpeed this.updateRadius() if (this.rad > 0) { this.updatePosition() this.draw() } else { this.init() } } } class Emitter { constructor() { this.particles = [...Array(numParticles).keys()].map( () => new Particle() ) } update() { this.particles.forEach(particle => particle.update()) } } const emitter = new Emitter() initCanvasColor = () => { ctx.fillStyle = `rgba(255, 255, 255, 1)` ctx.fillRect(0, 0, canvasDom.width, canvasDom.height) ctx.fillStyle = `rgba(0, 0, 0, 0.08)` ctx.fillRect(0, 0, canvasDom.width, canvasDom.height) } const render = () => { ctx.fillStyle = `rgba(0, 0, 0, 0.08)` ctx.fillRect(0, 0, canvasDom.width, canvasDom.height) emitter.update() requestAnimationFrame(render) } initCanvasColor() render() </script></body>
四、浏览器反对
- canvas的根底反对根本笼罩以后支流浏览器
- canvas在挪动端的兼容状况十分不错
- IE9以下浏览器能够思考应用 Explorercanvas
*目前3D(WebGL)和2D在一些老版本浏览器兼容有所不同
更多相干反对能够查看 caniuse
五、第三方库举荐
- IE9以下浏览器应用canvas Explorercanvas
- echart 可视化图表库
- paperjs 矢量图形脚本框架
- pixijs 2D sprite渲染引擎