|
@@ -0,0 +1,529 @@
|
|
|
+<template>
|
|
|
+ <div class="edit" @mouseup="mouseup">
|
|
|
+ <div class="main">
|
|
|
+ <h1 class="head">
|
|
|
+ 图像编辑器
|
|
|
+ <el-icon class="closeMain" @click="close"><Close /></el-icon>
|
|
|
+ </h1>
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="6" style="padding-left: 32px">
|
|
|
+ <h4>工具</h4>
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="12">
|
|
|
+ <span
|
|
|
+ @click="select[0] = 0"
|
|
|
+ :class="{ dark: true, active: select[0] === 0 }"
|
|
|
+ >
|
|
|
+ <el-icon><EditPen /></el-icon>
|
|
|
+ 绘制
|
|
|
+ </span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <span
|
|
|
+ @click="select[0] = 1"
|
|
|
+ :class="{ dark: true, active: select[0] === 1 }"
|
|
|
+ >
|
|
|
+ <el-icon><EditPen /></el-icon>
|
|
|
+ 橡皮擦
|
|
|
+ </span>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row style="margin-top: 1em">
|
|
|
+ <el-col :span="12">
|
|
|
+ <span
|
|
|
+ @click="select[0] = 2"
|
|
|
+ :class="{ dark: true, active: select[0] === 2 }"
|
|
|
+ >
|
|
|
+ <el-icon><EditPen /></el-icon>
|
|
|
+ 填充
|
|
|
+ </span>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <span
|
|
|
+ @click="select[0] = 3"
|
|
|
+ :class="{ dark: true, active: select[0] === 3 }"
|
|
|
+ >
|
|
|
+ <el-icon><EditPen /></el-icon>
|
|
|
+ 取色器
|
|
|
+ </span>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <h4>颜色</h4>
|
|
|
+ <el-color-picker v-model="select[1]" size="large" />
|
|
|
+ <div style="padding: 10px 0">
|
|
|
+ <div
|
|
|
+ :class="{ colorItm: true, active: select[1] === item }"
|
|
|
+ v-for="(item, i) in colors"
|
|
|
+ :key="i"
|
|
|
+ :style="'background-color:' + item"
|
|
|
+ @click="() => colorSelect(item)"
|
|
|
+ >
|
|
|
+ <el-icon color="#000" class="colorIcon" v-if="select[1] === item">
|
|
|
+ <Select />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <h4>画笔尺寸</h4>
|
|
|
+ <div>
|
|
|
+ <div
|
|
|
+ class="pen"
|
|
|
+ :style="{ width: item + 'px', height: item + 'px' }"
|
|
|
+ :class="{ active: select[2] === item }"
|
|
|
+ @click="select[2] = item"
|
|
|
+ v-for="(item, i) in width"
|
|
|
+ :key="'width' + i"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <h4>不透明度</h4>
|
|
|
+ <div class="racGroup">
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, btm0: true, active: select[3] === 1 }"
|
|
|
+ @click="() => selectBTM(1)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, btm1: true, active: select[3] === 0.8 }"
|
|
|
+ @click="() => selectBTM(0.8)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, btm2: true, active: select[3] === 0.6 }"
|
|
|
+ @click="() => selectBTM(0.6)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, btm3: true, active: select[3] === 0.4 }"
|
|
|
+ @click="() => selectBTM(0.4)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, btm4: true, active: select[3] === 0.2 }"
|
|
|
+ @click="() => selectBTM(0.2)"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <h4>清晰度</h4>
|
|
|
+ <div class="racGroup">
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, active: select[4] === 0 }"
|
|
|
+ @click="() => selectQXD(0)"
|
|
|
+ style="background-color: #292b2f"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, active: select[4] === 1 }"
|
|
|
+ @click="() => selectQXD(1)"
|
|
|
+ >
|
|
|
+ <div class="qxd" style="filter: blur(1px)"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, active: select[4] === 2 }"
|
|
|
+ @click="() => selectQXD(2)"
|
|
|
+ >
|
|
|
+ <div class="qxd" style="filter: blur(3px)"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, active: select[4] === 3 }"
|
|
|
+ @click="() => selectQXD(3)"
|
|
|
+ >
|
|
|
+ <div class="qxd" style="filter: blur(6px)"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="rav">
|
|
|
+ <div
|
|
|
+ :class="{ item: true, active: select[4] === 4 }"
|
|
|
+ @click="() => selectQXD(4)"
|
|
|
+ >
|
|
|
+ <div class="qxd" style="filter: blur(9px)"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <div style=" position: relative;width: 512px;height:512px;margin: 0 auto;">
|
|
|
+ <canvas
|
|
|
+ class="canvasBg"
|
|
|
+ :width="512"
|
|
|
+ :height="512"
|
|
|
+ ref="canvasBg"
|
|
|
+ ></canvas>
|
|
|
+ <canvas
|
|
|
+ :width="512"
|
|
|
+ :height="512"
|
|
|
+ @mousedown="mousedownFunc"
|
|
|
+ @mousemove="drawPen"
|
|
|
+ @mouseover="mouseover"
|
|
|
+ @mouseout="mouseout"
|
|
|
+ ref="canvas"
|
|
|
+ ></canvas>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <h4>操作</h4>
|
|
|
+
|
|
|
+ <span
|
|
|
+ class="dark"
|
|
|
+ style="
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 3px;
|
|
|
+ width: 80%;
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto;
|
|
|
+ "
|
|
|
+ @click="canvas.width = canvas.width"
|
|
|
+ >
|
|
|
+ <el-icon><EditPen /></el-icon>
|
|
|
+ 清除
|
|
|
+ </span>
|
|
|
+ <span
|
|
|
+ class="dark"
|
|
|
+ style="
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 3px;
|
|
|
+ width: 80%;
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-icon><EditPen /></el-icon>
|
|
|
+ 撤销
|
|
|
+ </span>
|
|
|
+ <span
|
|
|
+ class="dark"
|
|
|
+ style="
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 3px;
|
|
|
+ width: 80%;
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <el-icon><EditPen /></el-icon>
|
|
|
+ 重做
|
|
|
+ </span>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { defineProps, defineEmits, ref, onMounted } from 'vue';
|
|
|
+defineProps({
|
|
|
+ file: Object,
|
|
|
+});
|
|
|
+
|
|
|
+let mouseDown = false;
|
|
|
+const colors = [
|
|
|
+ 'rgb(234, 153, 153)',
|
|
|
+ 'rgb(224, 102, 102)',
|
|
|
+ 'rgb(204, 0, 0)',
|
|
|
+ 'rgb(153, 0, 0)',
|
|
|
+ 'rgb(102, 0, 0)',
|
|
|
+ 'rgb(249, 203, 156)',
|
|
|
+ 'rgb(246, 178, 107)',
|
|
|
+ 'rgb(230, 145, 56)',
|
|
|
+ 'rgb(180, 95, 6)',
|
|
|
+ 'rgb(120, 63, 4)',
|
|
|
+ 'rgb(255, 229, 153)',
|
|
|
+ 'rgb(255, 217, 102)',
|
|
|
+ 'rgb(241, 194, 50)',
|
|
|
+ 'rgb(191, 144, 0)',
|
|
|
+ 'rgb(127, 96, 0)',
|
|
|
+ 'rgb(182, 215, 168)',
|
|
|
+ 'rgb(147, 196, 125)',
|
|
|
+ 'rgb(106, 168, 79)',
|
|
|
+ 'rgb(56, 118, 29)',
|
|
|
+ 'rgb(39, 78, 19)',
|
|
|
+ 'rgb(164, 194, 244)',
|
|
|
+ 'rgb(109, 158, 235)',
|
|
|
+ 'rgb(60, 120, 216)',
|
|
|
+ 'rgb(17, 85, 204)',
|
|
|
+ 'rgb(28, 69, 135)',
|
|
|
+ 'rgb(180, 167, 214)',
|
|
|
+ 'rgb(142, 124, 195)',
|
|
|
+ 'rgb(103, 78, 167)',
|
|
|
+ 'rgb(53, 28, 117)',
|
|
|
+ 'rgb(32, 18, 77)',
|
|
|
+ 'rgb(213, 166, 189)',
|
|
|
+ 'rgb(194, 123, 160)',
|
|
|
+ 'rgb(166, 77, 121)',
|
|
|
+ 'rgb(116, 27, 71)',
|
|
|
+ 'rgb(76, 17, 48)',
|
|
|
+ 'rgb(255, 255, 255)',
|
|
|
+ 'rgb(192, 192, 192)',
|
|
|
+ 'rgb(131, 131, 131)',
|
|
|
+ 'rgb(82, 82, 82)',
|
|
|
+ 'rgb(0, 0, 0)',
|
|
|
+];
|
|
|
+const canvas = ref(null);
|
|
|
+const canvasBg = ref(null);
|
|
|
+let ctx = undefined;
|
|
|
+let ctxBg = undefined;
|
|
|
+const select = ref([0, 'rgb(241, 194, 50)', 6, 1, 0]);
|
|
|
+const emit = defineEmits(['closemain']);
|
|
|
+const close = () => {
|
|
|
+ emit('closemain');
|
|
|
+};
|
|
|
+
|
|
|
+// 线条集合
|
|
|
+const pathList = [];
|
|
|
+// 粗细
|
|
|
+const width = [6, 14, 16, 24, 30, 40, 48, 64];
|
|
|
+
|
|
|
+const selectBTM = index => {
|
|
|
+ select.value[3] = index;
|
|
|
+};
|
|
|
+
|
|
|
+const selectQXD = index => {
|
|
|
+ select.value[4] = index;
|
|
|
+};
|
|
|
+
|
|
|
+const colorSelect = color => {
|
|
|
+ select.value[1] = color;
|
|
|
+};
|
|
|
+
|
|
|
+const drawPen = ele => {
|
|
|
+ // 绘制
|
|
|
+ if (select.value[0] !== 0 || !mouseDown || !pathList.length) return;
|
|
|
+ pathList[pathList.length - 1].path.push({
|
|
|
+ x: ele.offsetX,
|
|
|
+ y: ele.offsetY,
|
|
|
+ });
|
|
|
+ const len = pathList[pathList.length - 1].path.length;
|
|
|
+ const last = {
|
|
|
+ offsetX: pathList[pathList.length - 1].path[len - 1].x,
|
|
|
+ offsetY: pathList[pathList.length - 1].path[len - 1].y,
|
|
|
+ };
|
|
|
+ ctx.quadraticCurveTo(last.offsetX, last.offsetY, ele.offsetX, ele.offsetY);
|
|
|
+ ctx.stroke();
|
|
|
+};
|
|
|
+
|
|
|
+const mousedownFunc = ele => {
|
|
|
+ // 鼠标按下
|
|
|
+ if (select.value[0] !== 0) return;
|
|
|
+ mouseDown = true;
|
|
|
+ start(ele);
|
|
|
+};
|
|
|
+
|
|
|
+const mouseup = () => {
|
|
|
+ // 鼠标松开
|
|
|
+ if (select.value[0] !== 0) return;
|
|
|
+ mouseDown = false;
|
|
|
+ ctx.closePath();
|
|
|
+ drawBg();
|
|
|
+};
|
|
|
+
|
|
|
+const mouseover = ele => {
|
|
|
+ // 鼠标移入
|
|
|
+ start(ele);
|
|
|
+};
|
|
|
+
|
|
|
+const start = ele => {
|
|
|
+ if (!mouseDown) return;
|
|
|
+ const w = canvas.value.width;
|
|
|
+ canvas.value.width = w;
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.lineCap = 'round';
|
|
|
+ ctx.lineJoin = 'round';
|
|
|
+ ctx.shadowBlur = select.value[4] / 4 + 1;
|
|
|
+ ctx.shadowColor = select.value[1];
|
|
|
+ ctx.strokeStyle = select.value[1];
|
|
|
+ console.log(select.value[3]);
|
|
|
+ ctx.globalAlpha = select.value[3];
|
|
|
+ ctx.lineWidth = select.value[2];
|
|
|
+ ctx.moveTo(ele.offsetX, ele.offsetY);
|
|
|
+ pathList.push({
|
|
|
+ globalAlpha: select.value[3],
|
|
|
+ shadowBlur: select.value[4] / 4 + 1,
|
|
|
+ strokeStyle: select.value[1],
|
|
|
+ shadowColor: select.value[1],
|
|
|
+ lineWidth: select.value[2],
|
|
|
+ lineCap: 'round',
|
|
|
+ lineJoin: 'round',
|
|
|
+ path: [{ x: ele.offsetX, y: ele.offsetY }],
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+const mouseout = () => {
|
|
|
+ // 鼠标移出
|
|
|
+ ctx.closePath();
|
|
|
+ drawBg();
|
|
|
+};
|
|
|
+
|
|
|
+//将线条绘制到背景canvas上
|
|
|
+const drawBg = () => {
|
|
|
+ console.log(pathList, ctxBg);
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ ctx = canvas.value.getContext('2d');
|
|
|
+ ctxBg = canvasBg.value.getContext('2d');
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.edit {
|
|
|
+ position: fixed;
|
|
|
+ z-index: 1;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ background-color: #00000060;
|
|
|
+ padding: 1em;
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+}
|
|
|
+
|
|
|
+.main {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ border-radius: 25px;
|
|
|
+ background-color: #2f3136;
|
|
|
+}
|
|
|
+h1 {
|
|
|
+ text-align: center;
|
|
|
+ padding: 10px 0;
|
|
|
+}
|
|
|
+h4 {
|
|
|
+ margin: 1em 0;
|
|
|
+}
|
|
|
+h4:first-child {
|
|
|
+ margin-top: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.closeMain {
|
|
|
+ float: right;
|
|
|
+ margin-right: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.racGroup {
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+.rav {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+.item {
|
|
|
+ position: relative;
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ border-radius: 16px;
|
|
|
+ background: #292b2f;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: opacity 0.25s;
|
|
|
+ border: 1px solid #292b2f;
|
|
|
+}
|
|
|
+
|
|
|
+.btm0 {
|
|
|
+ background: repeating-conic-gradient(
|
|
|
+ rgba(0, 0, 0, 0) 0%,
|
|
|
+ rgba(0, 0, 0, 0) 25%,
|
|
|
+ rgba(255, 255, 255, 0) 0%,
|
|
|
+ rgba(255, 255, 255, 0) 50%
|
|
|
+ )
|
|
|
+ 50% center / 10px 10px;
|
|
|
+}
|
|
|
+.btm1 {
|
|
|
+ background: repeating-conic-gradient(
|
|
|
+ rgba(0, 0, 0, 0.2) 0%,
|
|
|
+ rgba(0, 0, 0, 0.2) 25%,
|
|
|
+ rgba(255, 255, 255, 0.2) 0%,
|
|
|
+ rgba(255, 255, 255, 0.2) 50%
|
|
|
+ )
|
|
|
+ 50% center / 10px 10px;
|
|
|
+}
|
|
|
+.btm2 {
|
|
|
+ background: repeating-conic-gradient(
|
|
|
+ rgba(0, 0, 0, 0.4) 0%,
|
|
|
+ rgba(0, 0, 0, 0.4) 25%,
|
|
|
+ rgba(255, 255, 255, 0.4) 0%,
|
|
|
+ rgba(255, 255, 255, 0.4) 50%
|
|
|
+ )
|
|
|
+ 50% center / 10px 10px;
|
|
|
+}
|
|
|
+.btm3 {
|
|
|
+ background: repeating-conic-gradient(
|
|
|
+ rgba(0, 0, 0, 0.6) 0%,
|
|
|
+ rgba(0, 0, 0, 0.6) 25%,
|
|
|
+ rgba(255, 255, 255, 0.6) 0%,
|
|
|
+ rgba(255, 255, 255, 0.6) 50%
|
|
|
+ )
|
|
|
+ 50% center / 10px 10px;
|
|
|
+}
|
|
|
+.btm4 {
|
|
|
+ background: repeating-conic-gradient(
|
|
|
+ rgba(0, 0, 0, 0.8) 0%,
|
|
|
+ rgba(0, 0, 0, 0.8) 25%,
|
|
|
+ rgba(255, 255, 255, 0.8) 0%,
|
|
|
+ rgba(255, 255, 255, 0.8) 50%
|
|
|
+ )
|
|
|
+ 50% center / 10px 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.qxd {
|
|
|
+ background: #292b2f;
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ border-radius: 32px;
|
|
|
+}
|
|
|
+
|
|
|
+.pen {
|
|
|
+ border-radius: 50%;
|
|
|
+ background-color: #292b2f;
|
|
|
+ width: 6px;
|
|
|
+ height: 6px;
|
|
|
+ display: inline-block;
|
|
|
+ margin-right: 5px;
|
|
|
+}
|
|
|
+.colorItm {
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ margin: 0 20px 5px 0;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: inline-block;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+.colorIcon {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+}
|
|
|
+.dark {
|
|
|
+ background-color: #292b2f;
|
|
|
+ border-radius: 2em;
|
|
|
+ padding: 5px 0;
|
|
|
+ text-align: center;
|
|
|
+ width: 8em;
|
|
|
+ display: inline-block;
|
|
|
+ border: 1px solid #00000000;
|
|
|
+}
|
|
|
+.active {
|
|
|
+ border: 1px solid #3584e4;
|
|
|
+}
|
|
|
+
|
|
|
+.canvas{
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ background: #ffffff;
|
|
|
+}
|
|
|
+.canvasBg {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ background: #ffffff;
|
|
|
+}
|
|
|
+</style>
|