用canvas做画板,效果如下
直线
矩形
其他更多效果可以复制下面的demo尝试
demo
<template>
<div>
<div class="container">
<div class="row">
<ul class="type">
<li :class="{'active': chooseType === 'line'}" @click="chooseTypeFn('line')">直线</li>
<li :class="{'active': chooseType === 'rect'}" @click="chooseTypeFn('rect')">矩形</li>
<li :class="{'active': chooseType === 'circle'}" @click="chooseTypeFn('circle')">圆圈</li>
<li :class="{'active': chooseType === 'pen'}" @click="chooseTypeFn('pen')">铅笔</li>
<li :class="{'active': chooseType === 'curve'}" @click="chooseTypeFn('curve')">曲线</li>
<li :class="['poly',{'active': chooseType === 'poly'}]" @click="chooseTypeFn('poly')">多边形
<div :class="['bian',{'hide': chooseType !== 'poly'}]">
<span>边数</span><a-input-number type="number" :min="3" :max="15" v-model="sides" @change="changePoly"/>
</div>
</li>
<li :class="{'active': chooseType === 'eraser'}" @click="chooseTypeFn('eraser')">橡皮</li>
</ul>
<ul class="type">
<li :class="{'active': borderType === 'stroke'}" @click="chooseBorderTypeFn('stroke')">描边</li>
<li v-if="chooseType !== 'line' && chooseType !== 'pen'" :class="{'active': borderType === 'fill'}" @click="chooseBorderTypeFn('fill')">填充</li>
</ul>
<div class="space"></div>
<div class="box"><label><span>颜色</span></label><input type="color" name="color" v-model="color" @change="changeColor"></div>
<div class="box linewidth"><span>线宽</span><a-input-number type="number" :max="150" :min="1" v-model="lineWidth" @change="changeLineWidth"/></div>
<div class="space"></div>
<div class="clear shezhi" @click="plotFn('clear')">清空</div>
<div class="back shezhi" @click="plotFn('back')">撤销</div>
<div class="save shezhi" @click="plotFn('save')">保存</div>
</div>
</div>
<canvas ref="canvasPlot" :width="canvasWidth" :height="canvasHeight"></canvas>
</div>
</template>
<script>
function Draw (obj, setting) {
console.log(setting.type)
this.obj = obj
this.type = setting.type || 'stroke'
this.color = setting.color || '#f00'
this.width = setting.width || '2'
this.font = setting.font || '18px bold 黑体'
this.fillStyle = setting.fillStyle || '#f00'
this.textAlign = setting.textAlign || 'center'
this.textBaseline = setting.textBaseline || 'middle'
}
Draw.prototype = {
init () {
this.obj.strokeStyle = this.color
this.obj.lineWidth = this.width
this.obj.font = this.font
this.obj.fillStyle = this.fillStyle
this.obj.textAlign = this.textAlign
this.obj.textBaseline = this.textBaseline
},
rect (x, y, x1, y1) {
this.init()
this.obj.beginPath()
this.obj.rect(x, y, x1 - x, y1 - y)
if (this.type === 'stroke') {
this.obj.stroke()
} else if (this.type === 'fill') {
this.obj.fill()
}
},
circle (x, y, x1, y1) {
this.init()
const r = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2))
this.obj.beginPath()
this.obj.arc(x, y, r, 0, 2 * Math.PI)
if (this.type === 'stroke') {
this.obj.stroke()
} else if (this.type === 'fill') {
this.obj.fill()
}
},
line (x, y, x1, y1) {
this.init()
this.obj.beginPath()
this.obj.moveTo(x, y)
this.obj.lineTo(x1, y1)
this.obj.stroke()
},
text (content, x, y) {
this.init()
this.obj.fillText(content, x, y)
this.obj.stroke()
},
poly (x, y, x1, y1, n) {
this.init()
const obj = this.obj
const r = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2))
obj.save()
obj.translate(x, y)
obj.rotate(Math.PI / 2)
const nx = r * Math.cos(Math.PI / n)
const ny = r * Math.sin(Math.PI / n)
obj.beginPath()
obj.lineCap = 'round'
obj.moveTo(nx, ny)
for (let i = 0; i <= n; i++) {
obj.rotate(Math.PI * 2 / n)
obj.lineTo(nx, -ny)
}
if (this.type === 'stroke') {
this.obj.stroke()
} else if (this.type === 'fill') {
this.obj.fill()
}
obj.restore()
},
pen (x, y, x1, y1) {
this.init()
this.obj.save()
this.obj.lineCap = 'round'
this.obj.lineTo(x1, y1)
this.obj.stroke()
this.obj.restore()
},
eraser (x, y, x1, y1) {
this.obj.lineCap = 'round'
this.obj.clearRect(x1 - 5, y1 - 5, 10, 10)
},
cut (x, y, x1, y1) {
this.init()
this.obj.save()
this.obj.setLineDash([4, 2])
this.obj.beginPath()
this.obj.lineWidth = 1
this.obj.rect(x, y, x1 - x, y1 - y)
this.obj.stroke()
this.obj.restore()
},
curve (cp1x, cp1y, x, y) {
this.init()
this.obj.save()
this.obj.beginPath()
this.obj.moveTo(cp1y, y)
this.obj.quadraticCurveTo(cp1x, cp1y, x, y)
this.obj.stroke()
}
}
export default {
data () {
return {
chooseType: 'line',
sides: 3,
lineWidth: 1,
borderType: 'stroke',
color: '#f00',
plotContext: {}, // canvas对象
plotData: [],
plotCoord: {
x: 0,
y: 0,
x2: 0,
y2: 0
},
canvasWidth: 900,
canvasHeight: 600
}
},
watch: {
},
beforeCreate () {
},
beforeDestroy () {
},
mounted () {
this.initPlot()
},
created () {},
methods: {
chooseTypeFn (data) {
this.chooseType = data
},
changePoly (val) {
this.sides = val
},
chooseBorderTypeFn (val) {
this.borderType = val
},
changeColor (val) {
this.color = val
},
changeLineWidth (val) {
this.lineWidth = val
},
/**
* 绘图操作
* @param type clear:清空 back:撤回 save:保存
*/
plotFn (type) {
if (type === 'clear') {
this.plotData = []
this.plotContext.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
}
if (type === 'back') {
this.plotData.pop()
this.plotContext.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
if (this.plotData.length > 0) {
this.plotContext.putImageData(this.plotData[this.plotData.length - 1], 0, 0, 0, 0, this.canvasWidth, this.canvasHeight)
}
}
if (type === 'save') {
const reg = this.$refs.canvasPlot.toDataURL('image/png')
console.log(reg)
location.href = reg
}
},
initPlot () {
const _this = this
const canvas = this.$refs.canvasPlot
_this.plotContext = canvas.getContext('2d')
_this.$refs.canvasPlot.width = _this.canvasWidth
_this.$refs.canvasPlot.height = _this.canvasHeight
canvas.onmousedown = function (e) {
_this.plotCoord.x = e.offsetX
_this.plotCoord.y = e.offsetY
if (this.chooseType === 'pen') {
_this.plotContext.beginPath()
_this.plotContext.moveTo(_this.plotCoord.x, _this.plotCoord.y)
}
if (this.chooseType === 'eraser') {
_this.plotContext.clearRect(_this.plotCoord.x - 5, _this.plotCoord.y - 5, 10, 10)
}
// 实例化构造函数
const draw = new Draw(_this.plotContext, { type: _this.borderType, color: _this.color, width: _this.lineWidth })
canvas.onmousemove = function (e) {
_this.plotCoord.x2 = e.offsetX
_this.plotCoord.y2 = e.offsetY
if (_this.chooseType !== 'eraser') {
_this.plotContext.clearRect(0, 0, _this.canvasWidth, _this.canvasHeight)
if (_this.plotData.length !== 0) {
_this.plotContext.putImageData(_this.plotData[_this.plotData.length - 1], 0, 0, 0, 0, _this.canvasWidth, _this.canvasHeight)
}
}
if (_this.chooseType === 'poly') {
draw[_this.chooseType](_this.plotCoord.x, _this.plotCoord.y, _this.plotCoord.x2, _this.plotCoord.y2, _this.sides)
} else {
draw[_this.chooseType](_this.plotCoord.x, _this.plotCoord.y, _this.plotCoord.x2, _this.plotCoord.y2)
}
}
document.onmouseup = function () {
canvas.onmousemove = null
document.onmouseup = null
_this.plotData.push(_this.plotContext.getImageData(0, 0, _this.canvasWidth, _this.canvasHeight))
}
}
},
fnProcessHttpError (err) {
if (err.response) {
switch (err.response.code) {
default:
this.$error({
title: '错误',
content: err.response.message
})
break
}
} else {
this.$error({
title: '错误',
content: err.toString()
})
}
}
}
}
</script>
<style>
</style>
<style scoped>
body,html{ padding: 0;margin: 0;width: 100%; height: 100%; background-repeat: no-repeat; background-position: 0 0 no-repeat; background-size: cover; overflow: hidden; font-family: "Microsoft yahei"; background-color: #fff; }
.hide{display: none !important;}
.ant-input-number {width:45px;}
li{ list-style-type: none; }
ul{ padding-left: 10px }
canvas{ position: fixed; left: 280px; top: 5px; display: block; cursor: pointer; background-color: #FFFFFF; border: 1px solid #CCCCCC; }
.container{
width:260px;
height:60px;
padding: 10px;
}
.container h2{
width:100%;
height:60px;
font-size:22px;
text-align:center;
color: #ff377e;
font-weight:normal;
line-height:60px;
border-bottom:1px solid #000;
}
.container h3{
width:100%;
height:30px;
font-size:18px;
text-align:center;
color: #41b1ff;
font-weight:normal;
line-height:30px;
}
.row{ width: 100%; height: auto; background-color: rgba(192,192,192,0.50); overflow: hidden; }
.type{
width:100%;
height:auto;
margin:20px 0;
padding-bottom:10px;
border-bottom:1px solid #fff;
}
.type:after{
content:"";
display:block;
clear:both;
}
.type li{
float:left;
width:90px;
height:45px;
margin:10px;
background-color:#0078ff;
color:#fff;
font-size:18px;
line-height:45px;
text-align:center;
border-radius:5px;
cursor:pointer;
transition:all 0.7s;
}
.type li:hover{
background-color:#fff;
color:#0078ff;
box-shadow:0 0 10px rgba(0, 120, 255, 0.77);
}
.type .active{
background-color:#fff;
color:#0078ff;
box-shadow:0 0 10px rgba(0, 120, 255, 0.77);
}
.style{
width:100%;
height:40px;
}
.style li{
float:left;
width:90px;
height:45px;
background-color:#FFBC00;
color:#fff;
font-size:18px;
line-height:45px;
text-align:center;
border-radius:5px;
cursor:pointer;
transition:all 0.7s;
}
.style li:hover{
color: #ffa800;
background-color:#fff;
box-shadow:0 0 10px rgba(255, 232, 95, 0.77);
}
.style .active{
color: #ffa800;
background-color:#fff;
box-shadow:0 0 10px rgba(255, 232, 95, 0.77);
}
.style li:nth-child(1){
margin-left:25px;
}
.style li:nth-child(2){
margin-left:20px;
}
.poly{
position:relative;
}
.bian{
position:absolute;
left:100px;top:0;
width:90px;
height:auto;
border-radius:5px;
background-color:#EC494E;
overflow:hidden;
transform:scale(1.2);
}
.bian span{
display:block;
float:left;
width:40px;
height:45px;
font-size:16px;
color:#fff;
text-align:center;
line-height:40px;
color:#fff;
transition:all 0.7s;
}
.bian input{
float:left;
width:45px;
height:35px;
margin-top:5px;
border:1px solid #fff;
box-sizing:border-box;
text-align:center;
line-height:40px;
font-size:18px;
color:#fff;
background:#EC494E;
border-radius:3px;
box-shadow:0 0 0 4px #fff inset;
transition:all 0.7s;
}
.cut,.create,.back{
float:left;
width:90px;
height:45px;
margin-left:25px;
margin-bottom:15px;
background-color: #5bd219;
color:#fff;
font-size:18px;
line-height:45px;
text-align:center;
border-radius:5px;
cursor:pointer;
}
.copy,.clear,.save{
float:left;
width:90px;
height:45px;
margin-left:20px;
margin-bottom:15px;
background-color: #5bd219;
color:#fff;
font-size:18px;
line-height:45px;
text-align:center;
border-radius:5px;
cursor:pointer;
}
.shezhi{
transition:all 0.7s;
}
.shezhi.active, .shezhi:hover{
background:#fff;
color:#5bd219;
}
.create{
position:relative;
}
.xinjian{
position:absolute;
left:0;top:0;
width:200px;
height:165px;
border-radius:5px;
background-color:#EC494E;
box-shadow:0 0 15px rgba(236, 73, 78, 0.76);
cursor:default;
display:none;
}
.xinjian:hover{
color:#fff;
}
.xinjian_before{
position:absolute;
right:0;
top:0;
width:20px;
height:20px;
color:#fff;
line-height:16px;
text-indent:-1px;
font-size:30px;
border:1px solid #fff;
transform:rotate(45deg);
border-radius:50%;
cursor:pointer;
}
.xinjian h6{
width:100%;
height:50px;
color:#fff;
line-height:50px;
text-align:center;
font-size:20px;
font-weight:normal;
}
#ding{
width:60px;
height:30px;
font-size:14px;
border-radius:3px;
color:#fff;
background-color:#0078ff;
border:0;
outline:none;
cursor:pointer;
box-shadow:0 0 10px rgba(0, 120, 255, 0.5);
}
.xinjian_width,.xinjian_height{
float:left;
width:85px;
height:40px;
margin-left:10px;
border-radius:5px;
margin-bottom:20px;
background-color:#FFBC00;
}
.xinjian_width:hover,.xinjian_height:hover{
background-color:#fff;
color:#000;
}
.xinjian_width span,.xinjian_height span{
float:left;
width:30px;
height:100%;
font-size:16px;
text-align:center;
line-height:40px;
}
.xinjian_width input,.xinjian_height input{
float:left;
margin-top:5px;
width:50px;
height:30px;
text-align:center;
line-height:30px;
font-size:14px;
border:1px solid #FFBC00;
box-sizing:border-box;
}
.box{
float:left;
width:90px;
height:auto;
background-color:#EC494E;
overflow:hidden;
border-radius:5px;
transition:all 0.7s;
}
.box{
margin-left:25px;
}
.linewidth{
margin-left:20px;
}
.box:hover{
color:#f00;
background-color:#fff;
}
.box span{
display:block;
float:left;
width:40px;
height:45px;
font-size:16px;
color:#fff;
text-align:center;
line-height:40px;
transition:all 0.3s;
}
.box:hover span{color:#f00;}
.box input{
float:left;
width:45px;
height:35px;
margin-top:5px;
border:1px solid #fff;
box-sizing:border-box;
text-align:center;
line-height:40px;
font-size:18px;
color:#fff;
background:#EC494E;
border-radius:3px;
box-shadow:0 0 0 4px #fff inset;
transition:all 0.3s;
}
.box2 span{
width:55px;
}
.box2 input{
width:140px;
}
.space{
float:left;
width:100%;
height:0;
border-bottom:1px solid #fff;
margin:20px 0;
}
</style>
demo还有很多不足之处,欢迎小伙伴纠错,O(∩_∩)O~