You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

391 lines
11 KiB

<template>
<div>
<!-- 画布 -->
<div style="padding: 5px" @mousemove="canvasOutMove($event)" @touchmove="canvasOutMove($event)">
<div class="graffiti-container">
<canvas id="canvas"
width="766"
height="400"
@mousedown="canvasDown($event)"
@mouseup="canvasUp($event)"
@mousemove="canvasMove($event)"
@touchstart="canvasDown($event)"
@touchend="canvasUp($event)"
@touchmove="canvasMove($event)">
</canvas>
</div>
</div>
<div style="padding: 10px 0 0 5px;">
<!-- 颜色 -->
<div class="graffiti-tools">
<span class="graffiti-title">画笔颜色</span>
<div class="myCenter" style="margin-left: 2rem">
<div v-for="(color, index) in colors"
:class="{ activeColor: config.lineColor === color }"
:style="{ background: color }"
@click="setColor(color)"
class="graffiti-color"
:key="index">
</div>
</div>
</div>
<!-- 大小 -->
<div class="graffiti-tools">
<span class="graffiti-title">画笔大小</span>
<div class="myCenter" style="margin-left: 2rem">
<i v-for="(pen, index) in brushSize"
:key="index"
class="graffiti-size"
:class="[pen.className, { activeSize: config.lineWidth === pen.lineWidth }]"
@click="setBrush(pen.lineWidth)">
</i>
</div>
</div>
<!-- 操作 -->
<div class="graffiti-tools">
<span class="graffiti-title">操作</span>
<div class="myCenter" style="margin-left: 3.7rem">
<i v-for="(control, index) in controls"
:title="control.title"
:class="control.className"
class="graffiti-operate"
@click="controlCanvas(control.action)"
:key="index">
</i>
</div>
</div>
<!-- -->
<div class="graffiti-tools" style="justify-content: center">
<proButton :info="'文字'"
@click.native="showComment()"
:before="$constant.before_color_1"
:after="$constant.after_color_1"
style="margin-right: 6px">
</proButton>
<proButton :info="'提交'"
@click.native="getImage()"
:before="$constant.before_color_2"
:after="$constant.after_color_2">
</proButton>
</div>
</div>
</div>
</template>
<script>
const proButton = () => import( "../common/proButton");
export default {
components: {
proButton
},
data() {
return {
token: "",
context: {},
canvasMoveUse: false,
// 存储当前表面状态数组-上一步
preDrawAry: [],
// 存储当前表面状态数组-下一步
nextDrawAry: [],
// 中间数组
middleAry: [],
// 配置参数
config: {
lineWidth: 5,
lineColor: "#8154A3",
shadowBlur: 2,
},
colors: ["#8154A3", "#fef4ac", "#0018ba", "#ffc200", "#f32f15", "#cccccc", "#5ab639"],
brushSize: [{
className: "small el-icon-edit",
lineWidth: 5,
}, {
className: "middle el-icon-edit",
lineWidth: 10,
}, {
className: "big el-icon-edit",
lineWidth: 15,
}]
};
},
computed: {
controls() {
return [{
title: "上一步",
action: "prev",
className: this.preDrawAry.length
? "active el-icon-arrow-left"
: "ban el-icon-arrow-left",
}, {
title: "下一步",
action: "next",
className: this.nextDrawAry.length
? "active el-icon-arrow-right"
: "ban el-icon-arrow-right",
}, {
title: "清除",
action: "clear",
className:
this.preDrawAry.length || this.nextDrawAry.length
? "active el-icon-refresh"
: "ban el-icon-refresh",
}];
}
},
mounted() {
const canvas = document.querySelector("#canvas");
this.context = canvas.getContext("2d", {willReadFrequently: true});
this.initDraw();
this.setCanvasStyle();
},
created() {
if (!this.$common.isEmpty(this.$store.state.currentUser) && !this.$common.isEmpty(this.$store.state.currentUser.email)) {
this.getUpToken();
}
},
methods: {
getUpToken() {
this.$http.get(this.$constant.baseURL + "/qiniu/getUpToken")
.then((res) => {
if (!this.$common.isEmpty(res.data)) {
this.token = res.data;
}
})
.catch((error) => {
this.$message({
message: error.message,
type: "error"
});
});
},
canvasOutMove(e) {
const canvas = document.querySelector("#canvas");
if (e.target !== canvas) {
this.canvasMoveUse = false;
}
},
initDraw() {
const preData = this.context.getImageData(0, 0, 1200, 600);
// 空绘图表面进栈
this.middleAry.push(preData);
},
canvasUp(e) {
const preData = this.context.getImageData(0, 0, 1200, 600);
if (!this.nextDrawAry.length) {
// 当前绘图表面进栈
this.middleAry.push(preData);
} else {
this.middleAry = [];
this.middleAry = this.middleAry.concat(this.preDrawAry);
this.middleAry.push(preData);
this.nextDrawAry = [];
}
this.canvasMoveUse = false;
this.context.beginPath();
},
canvasDown(e) {
this.canvasMoveUse = true;
// client是基于整个页面的坐标
// offset是canvas距离顶部以及左边的距离
this.setCanvasStyle();
// 清除子路径
this.context.beginPath();
this.context.moveTo(e.layerX, e.layerY);
// 当前绘图表面状态
const preData = this.context.getImageData(0, 0, 1200, 600);
// 当前绘图表面进栈
this.preDrawAry.push(preData);
},
canvasMove(e) {
if (this.canvasMoveUse) {
this.context.lineTo(e.layerX, e.layerY);
this.context.stroke();
}
},
// 设置绘画配置
setCanvasStyle() {
this.context.lineWidth = this.config.lineWidth;
this.context.shadowBlur = this.config.shadowBlur;
this.context.shadowColor = this.config.lineColor;
this.context.strokeStyle = this.config.lineColor;
},
// 设置颜色
setColor(color) {
this.config.lineColor = color;
},
// 设置笔刷大小
setBrush(size) {
this.config.lineWidth = size;
},
controlCanvas(action) {
switch (action) {
case "prev":
if (this.preDrawAry.length) {
const popData = this.preDrawAry.pop();
const midData = this.middleAry[this.preDrawAry.length + 1];
this.nextDrawAry.push(midData);
this.context.putImageData(popData, 0, 0);
}
break;
case "next":
if (this.nextDrawAry.length) {
const popData = this.nextDrawAry.pop();
const midData = this.middleAry[this.middleAry.length - this.nextDrawAry.length - 2];
this.preDrawAry.push(midData);
this.context.putImageData(popData, 0, 0);
}
break;
case "clear":
this.clearContext();
this.middleAry = [this.middleAry[0]];
break;
}
},
clearContext() {
this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
this.preDrawAry = [];
this.nextDrawAry = [];
},
showComment() {
this.clearContext();
this.$emit("showComment");
},
getImage() {
if (this.$common.isEmpty(this.$store.state.currentUser)) {
this.$message({
message: "请先登录!",
type: "error"
});
return;
}
if (this.$common.isEmpty(this.token)) {
this.$message({
message: "上传出错!",
type: "warning"
});
return;
}
if (this.preDrawAry.length < 1) {
this.$message({
message: "你还没画呢~",
type: "warning",
});
return;
}
const canvas = document.querySelector("#canvas");
const dataURL = canvas.toDataURL("image/png");
let arr = dataURL.split(","),
mine = arr[0].match(/:(.*?);/)[1],
str = atob(arr[1]),
n = str.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = str.charCodeAt(n);
}
let obj = new Blob([u8arr], {type: mine});
let fd = new FormData();
fd.append("file", obj);
fd.append("token", this.token);
fd.append("key", "graffiti" + "/" + this.$store.state.currentUser.username.replace(/[^a-zA-Z]/g, '') + this.$store.state.currentUser.id + new Date().getTime() + Math.floor(Math.random() * 1000) + ".png");
this.$http.uploadQiniu(this.$constant.qiniuUrl, fd)
.then((res) => {
if (!this.$common.isEmpty(res.key)) {
this.clearContext();
let url = this.$constant.qiniuDownload + res.key;
this.$common.saveResource(this, "graffiti", url, obj.size, obj.type);
let img = "<你画我猜," + url + ">";
this.$emit("addGraffitiComment", img);
}
})
.catch((error) => {
this.$message({
message: error.message,
type: "error"
});
});
}
}
}
</script>
<style scoped>
.graffiti-container {
overflow: hidden;
border: 2px var(--lightGray) solid;
border-radius: 4px;
}
.graffiti-tools {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.graffiti-title {
font-size: 14px;
}
.graffiti-color {
width: 1rem;
height: 1rem;
border-radius: 50%;
float: left;
margin-right: 20px;
cursor: pointer;
transition: all 0.2s;
}
.graffiti-color:hover {
width: 1.3rem;
height: 1.3rem;
}
.activeColor {
width: 1.3rem;
height: 1.3rem;
}
.graffiti-size {
cursor: pointer;
margin-right: 20px;
}
.graffiti-size.small {
font-size: 14px;
}
.graffiti-size.middle {
font-size: 16px;
}
.graffiti-size.big {
font-size: 18px;
}
.activeSize {
background-color: var(--themeBackground);
color: var(--white);
border-radius: 50%;
padding: 2px;
}
.graffiti-operate {
margin-right: 20px;
cursor: pointer;
font-size: 18px;
}
.graffiti-operate.ban {
cursor: not-allowed
}
</style>