前端canvas 库
chart.js 图表 phaser features 动画游戏库
基础
注意: 先是canvas属性的宽高,再根据 css 定义的宽高进行(宽高)缩放(缩放到 css 宽高大小,因此可能会变形)
var canvas=document.getElementById('mycanvas')
var ctx=canvas.getContext('2d')
ctx.moveTo(0,0)
ctx.lineTo(100,100)
ctx.stroke()
一些 api
//路径
ctx.moveTo()
ctx.lineTo() //画直线
ctx.arc(300,300,radius,0,2*Math.PI,true) //画圆
ctx.rect()
// 样式
ctx.lineWidth=10
ctx.strokeStyle='#aaa'
ctx.fillStyle='rgba(0,244,244,1)'
// 填充和描边
ctx.stoke() //描边(要避免重复绘制)
ctx.fill() //填充
ctx.strokeRect(1,2,3,4) //画矩形
ctx.fillRect(1,2,3,4)
// 路径开始和闭合
ctx.beginPath() //重新开始一条路径
ctx.closePath() //闭合路径(路径不会断开)
//坐标系原点操作(针对坐标系变换)
ctx.translate(x,y) //平移
ctx.rotate(Math.PI/4) //旋转(顺时针)
ctx.scale(x,y)//当前坐标系进行 x,y 缩放
//保存上下文环境栈(样式和坐标转换)
ctx.save() //保存上面的上下文环境,入栈
ctx.restore() // 上下文环境出栈
// 渐变
//线性渐变
linearGradient= ctx.createLinearGradient(x1,y1,x2,y2)
linearGradient.addColorStop(0,'rgb(255,0,0)')
linearGradient.addColorStop(0.5,'rgb(255,0,255)')
ctx.fillStyle=linearGradient
ctx.fillRect(x1,y1,x2,y2)
//径向渐变(两个圆半径圈都是分界点)
radiaGradient=ctx.createRadialGradient(x1,y1,raidus,x2,y2,radius)
radiaGradient.addColorStop(0,'#ccc')
// 文字
ctx.font='50px 字体'
ctx.textBaseline='top' / top middle bottom
ctx.textAlign='center' //设置文字坐标是基于什么,默认时左
ctx.fillText(str,x,y)
ctx.strokeText(str,x,y)
ctx.measureText(str).width //获取文本的宽度
// 图片绘制
var img=new Image()
img.src=''
img.onload=function(){
ctx.drawImage(img,x1,y1)
ctx.drawImage(img,x1,y1,width,height) //坐标和缩放
// 参数解释(img,原图起始x,原图起始 y,原图截取的宽,原图截取的高,画布上的 x,画布上的 y,画布上缩放的 width,画布上缩放的 height)
ctx.drawImage(img,sx1,sy1,swidth,sheight,x2,y2,width,height)
}
//图形画刷
var img=new img()
img.src=''
img.onload=function(){
//四中模式 no-repeat ,repeat-x ,repeat-y ,repeat
pattern = ctx.createPattern(img,'repeat')
ctx.fillStyle=pattern
}
// canvas 剪辑区域(配合 save 和 restore 来处理)
ctx.clip() //区域剪辑,只能在之前设置的路径中进行填充和描边
// 阴影(也可以是对文字)
ctx.shadowOffsetX=10
ctx.shadowOffsetY=10
ctx.shadowColor=''
ctx.shadowBlur=10 //模糊程度
ctx.fillStyle=''
// 曲线绘制
//圆弧
ctx.arc(x,y,radius,起始角度,结束角度,true(逆时针))
//贝塞尔曲线(三个点)
ctx.moveTo(x1,y1)
ctx.quadraticCurveTo(x2,y2,x3,y3)
//二次贝塞尔曲线(四个点)
ctx.moveTo(x1,y1)
ctx.bezierCurveTo(x2,y2,x3,y3,x4,y4)
//清空画布
ctx.clearRect()
// 动画(一帧帧的合成,改变变量画好每一帧)
// 交互,改变动画变量和控制变量(将动画绘制变量抽离出来单独处理,而不是停止定时器之类)
canvas.onmousemove=function(e){
mouseX=e.offsetX
mouseY=e.offsetY
if(判断条件){
isActive=false
}else{
isActive=true
}
}
setInterveal(function(){
if(isActive){
ctx.clearRect(0,0,canvas.width,canvas.height) //清空画布
//画一帧
}
})
// 离屏(无需在界面中显示)canvas技术
(1) 绘制 canvas1画布的图像(很复杂)
canvas1=document.getElementById('canvas1')
canvas1.getContext('2d')
(2) 在 canvas2中把 cavas1作为图片绘制(作为背景或其他,很复杂的 canvas1绘制图形操作就只执行一次,其他都是绘制整个已有的 canvas1图片)
ctx.drawImage(canvas1,0,0,canvas1.width,canvas1.height,0,0,canvas.width,canvas.height)
canvas 绘制手势解锁
(function(w){
/**
* 实现画圆和划线:
* 1、添加事件touchstart、touchmove、touchend
* 2、touchstart判断是否点击的位置处于圆内getPosition,处于则初始化
* lastpoint、restPoint
* 3、touchmove做的就是:画圆drawPoint和画线drawLine
*
* 实现自动画圆的效果
* 1、检测手势移动的位置是否处于圆内
* 2、圆内的话则画圆 drawPoint
* 3、已经画过实心圆的圆,无需重复检测
*
* 实现解锁成功:
* 1、检测路径是否是对的
* 2、如果是对的就重置,圆圈变绿
* 3、不对也重置,圆圈变红
* 4、重置
*/
w.canvasLock=function(obj){
this.height=obj.height
this.width=obj.width
this.chooseType=obj.chooseType
}
// js 方式动态生成 dom
canvasLock.prototype.initDom=function(){
var wrap=document.createElement('div')
var str=`<h4 id="title" class="title">绘制解锁图案</h4>`
wrap.setAttribute('style','position: absolute;top:0;left:0;right:0;bottom:0;');
var canvas = document.createElement('canvas');
canvas.setAttribute('id','canvas');
canvas.style.cssText = 'background-color: #305066;display: inline-block;margin-top: 15px;';
wrap.innerHTML = str;
wrap.appendChild(canvas);
var width = this.width || 300;
var height = this.height || 300;
document.body.appendChild(wrap);
// 高清屏锁放
canvas.style.width = width + "px";
canvas.style.height = height + "px";
canvas.width = width;
canvas.height = height;
}
// 画圆
canvas.prototype.drawCle=function(x,y){
this.ctx.strokeStyle = '#CFE6FF';
this.ctx.lineWidth = 2;
this.ctx.beginPath();
this.ctx.arc(x, y, this.r, 0, Math.PI * 2, true);
this.ctx.closePath();
this.ctx.stroke();
}
// 创建解锁点的坐标,根据canvas的大小来平均分配半径
canvasLock.prototype.createCircle = function() {
var n = this.chooseType;
var count = 0;
this.r = this.ctx.canvas.width / (2 + 4 * n);// 公式计算
this.lastPoint = [];
this.arr = [];
this.restPoint = [];
var r = this.r;
for (var i = 0 ; i < n ; i++) {
for (var j = 0 ; j < n ; j++) {
count++;
var obj = {
x: j * 4 * r + 3 * r,
y: i * 4 * r + 3 * r,
index: count
};
this.arr.push(obj);
this.restPoint.push(obj);
}
}
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
for (var i = 0 ; i < this.arr.length ; i++) {
// 画圆函数
this.drawCle(this.arr[i].x, this.arr[i].y);
}
}
// 程序初始化
canvasLock.prototype.init = function() {
this.initDom();
this.canvas = document.getElementById('canvas');
this.ctx = this.canvas.getContext('2d');
this.touchFlag = false;
// 1、确定半径
// 2、确定每一个圆的中心坐标点
// 3、一行3个圆14个半径,一行4个圆有18个半径
this.createCircle();
this.bindEvent();
}
canvasLock.prototype.bindEvent=function(){
var self = this;
this.canvas.addEventListener("touchstart", function (e) {
// touchstart判断是否点击的位置处于圆内getPosition,处于则初始化
// lastpoint、restPoint
// po有x和y,并且是相较于canvas边距
var po = self.getPosition(e);
// 判断是否在圆内的原理:多出来的这条 x/y < r 在圆内
for (var i = 0 ; i < self.arr.length ; i++) {
if (Math.abs(po.x - self.arr[i].x) < self.r && Math.abs(po.y - self.arr[i].y) < self.r) {
self.touchFlag = true;
// lastPoint存放的就是选中的圆圈的x/y坐标值
self.lastPoint.push(self.arr[i]);
self.restPoint.splice(i,1);
break;
}
}
}, false);
// 每touchmove 一次,就清空画布绘制一次
this.canvas.addEventListener("touchmove", function (e) {
// touchmove做的就是:画圆drawPoint和划线drawLine
if (self.touchFlag) {
self.update(self.getPosition(e));
}
}, false);
// touchend 检测
this.canvas.addEventListener("touchend", function(e){
if (self.touchFlag) {
self.storePass(self.lastPoint);
setTimeout(function(){
self.reset();
}, 300);
}
}, false);
}
// 获取touch点相对于canvas的坐标
canvasLock.prototype.getPosition = function(e) {
var rect = e.currentTarget.getBoundingClientRect();
var po = {
x: (e.touches[0].clientX - rect.left),
y: (e.touches[0].clientY - rect.top)
};
return po;
}
// 核心变换方法在touchmove时候调用
canvasLock.prototype.update = function(po) {
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
// 重新画9个圆圈
for (var i = 0 ; i < this.arr.length ; i++) { // 每帧先把面板画出来
this.drawCle(this.arr[i].x, this.arr[i].y);
}
// 1、检测手势移动的位置是否处于下一个圆内
// 2、圆内的话则画圆 drawPoint
// 3、已经画过实心圆的圆,无需重复检测
for (var i = 0 ; i < this.restPoint.length ; i++) {
if (Math.abs(po.x - this.restPoint[i].x) < this.r && Math.abs(po.y - this.restPoint[i].y) < this.r) {
this.lastPoint.push(this.restPoint[i]);
this.restPoint.splice(i, 1);
break;
}
}
this.drawPoint();// 画圆
this.drawLine(po);// 画线
}
// 初始化圆心
canvasLock.prototype.drawPoint = function() {
for (var i = 0 ; i < this.lastPoint.length ; i++) {
this.ctx.fillStyle = '#CFE6FF';
this.ctx.beginPath();
this.ctx.arc(this.lastPoint[i].x, this.lastPoint[i].y, this.r / 2, 0, Math.PI * 2, true);
this.ctx.closePath();
this.ctx.fill();
}
}
// 画线
canvasLock.prototype.drawLine = function(po) {
this.ctx.beginPath();
this.ctx.lineWidth = 3;
this.ctx.moveTo(this.lastPoint[0].x, this.lastPoint[0].y);
for (var i = 1 ; i < this.lastPoint.length ; i++) {
this.ctx.lineTo(this.lastPoint[i].x, this.lastPoint[i].y);
}
this.ctx.lineTo(po.x, po.y);
this.ctx.stroke();
this.ctx.closePath();
}
// 1、检测路径是否是对的
// 2、如果是对的就重置,圆圈变绿
// 3、不对也重置,圆圈变红
// 4、重置
canvasLock.prototype.storePass = function() {
if (this.checkPass()) {
document.getElementById('title').innerHTML = '解锁成功';
this.drawStatusPoint('#2CFF26');
}else{
document.getElementById('title').innerHTML = '解锁失败';
this.drawStatusPoint('red');
}
}
canvasLock.prototype.checkPass = function() {
var p1 = '123',
p2 = '';
for (var i = 0 ; i < this.lastPoint.length ; i++) {
p2 += this.lastPoint[i].index;
}
return p1 === p2;
}
canvasLock.prototype.drawStatusPoint = function(type) {
for (var i = 0 ; i < this.lastPoint.length ; i++) {
this.ctx.strokeStyle = type;
this.ctx.beginPath();
this.ctx.arc(this.lastPoint[i].x, this.lastPoint[i].y, this.r, 0, Math.PI * 2, true);
this.ctx.closePath();
this.ctx.stroke();
}
}
canvasLock.prototype.reset = function(){
this.createCircle();
}
}(window))