背景
这是这周帮其他中心搞的项目,很有意思的一个项目。
输入,canvas随笔画,传入给服务端图片和一些操作历史数据。
服务端根据训练模型,人工智能,渲染生成一些效果图。
等于就是简单画几笔,让你也能画出牛逼的画。
需求
也很简单,给了我一张示意图。
那就开干呗,把canvas的东西整合一下就行。
细节和代码
-
操作区 range、select、做选择为主,确定crtColor,crtType, crtBrushStyle, crtLineW
-
画板区 其实就很简单,onmousedown的时候记录flag=true,onmousemove的时候不断绘制,onmouseup的时候把flag=falase
-
结果区 拿服务端渲染的图片回来展示就行
-
细节1 在mousemove的时候,持续划线即可。这样滑板上就会有数据填充
// 创建路径
ctx.beginPath()
// 绘制线条
ctx.moveTo(startX, startY)
ctx.lineTo(endX, endY)
// 设置线条大小与颜色
ctx.strokeStyle = that.crtColor
ctx.lineWidth = that.crtLineW
// 设置线两端形状
ctx.lineCap = that.crtBrushStyle
ctx.stroke()
// 关闭路径
ctx.closePath()
- 细节2 最后提交和history提交都是以base64提交图像 所以canvas需要转成base64
var mycanvas = document.getElementById('canvas')
obj.dataURL = mycanvas.toDataURL()
其他没啥了,自己看源码,在vue中可以跑起来的。
源码
发现没法上传附件啊,只能上传链接,不太合理
<template>
<div class="app-container">
<!--第0行-->
<div v-if="resList.length>0" class="resDiv">
<div
v-for="(item, i) of resList"
:key="i"
class="res"
:style="{ background: item }"
>
结果{{ i }}
</div>
</div>
<!--第一行-->
<table class="table" style="margin-top:20px">
<tr>
<td width="11%" valign="center">
<h4>Fill/brush color:</h4>
</td>
<td width="4%" valign="center">
<!-- <canvas id="brush_color" /> -->
<div id="crtColor" style="width: 20px; height: 20px" />
</td>
<td width="11%" valign="center">
<h4>Brush shape:</h4>
</td>
<td width="10%">
<button
id="brush_round"
class="btn brush_circle"
:class="{ 'brush-active': crtBrushStyle === 'round' }"
@click="setBrushStyle('round')"
/>
<button
id="brush_square"
class="btn brush_square"
:class="{ 'brush-active': crtBrushStyle === 'square' }"
@click="setBrushStyle('square')"
/>
<!-- <button id="brush_diamond" class="btn brush_diamond" /> -->
</td>
<td width="9%">
<h4>Brush size:</h4>
</td>
<td width="4%">
<h4>
<span id="rangevalue" />
</h4>
</td>
<td>
<el-slider v-model="crtLineW" :max="200" :step="2" />
</td>
</tr>
</table>
<!--第二行-->
<table id="main_frame" class="table" style="position: relative">
<tr>
<!--第一列-->
<td width="12%" valign="top">
<div id="palette" class="btn-group">
<div
v-for="(item, i) of menuList"
:key="i"
:class="'item lbl-' + item.key"
@click="setMenu(item)"
>
{{ item.name }}
</div>
</div>
</td>
<td valign="top" width="12%" class="td" align="center">
<div class="btn-group2">
<div>
<button id="new" class="btn new" @click="clearCanvas" />
</div>
<div>
<button id="brush" class="btn brush" />
</div>
<!-- <tr><th><button id="fill" class="btn fill" /></th></tr>
<tr><th><button id="eyedropper" class="btn eyedropper" /></th></tr>
<tr><th><button id="undo" class="btn undo" /></th></tr>-->
</div>
</td>
<td align="center" class="center">
<canvas id="canvas" class="viewport" width="800" height="500" />
<el-button
type="primary"
style="margin-top: 10px; width: 100px"
@click="submit"
>生成</el-button>
</td>
<!-- <td valign="bottom" algin="left">
<a id="download" download="gaugan_input.png" href="" onclick="download_segmap(this);"><button id="save" class="btn save" /></a>
</td>-->
</tr>
</table>
<!--第三行-->
<br>
</div>
</template>
<script>
import { submitData } from '@/api/user'
export default {
data() {
return {
menuList: [],
crtType: null,
crtColor: null,
crtLineW: 24,
crtBrushStyle: 'round',
dataURL: '',
historyArr: [],
resList: []
}
},
mounted() {
this.menuList = [
{
name: '房屋',
key: 'house',
color: '#7f4502'
},
{
name: '山峰',
key: 'hill',
color: '#7ec864'
},
{
name: '道路',
key: 'road',
color: '#946e28'
},
{
name: '河流',
key: 'river',
color: '#9ac6da'
},
{
name: '绿植',
key: 'grass ',
color: '#7bc800'
}
]
this.initCanvas()
},
methods: {
setMenu(item) {
document.getElementById('crtColor').style.backgroundColor = item.color
this.crtColor = item.color
this.crtType = item.name
},
setBrushStyle(s) {
this.crtBrushStyle = s
},
initCanvas() {
// 得到canvas
var mycanvas = document.getElementById('canvas')
// 得到绘制对象
var ctx = mycanvas.getContext('2d')
// 定义变量接受绘制什么图像
var data_style = 'brush'
// 定义判断鼠标是否按下事件
var flag = false
// 监听canva鼠标按下事件
var startX = 0
var startY = 0
var endX = 0
var endY = 0
var that = this
let startPos, endPos
let posList = []
let lastTs = new Date().getTime()
mycanvas.onmousedown = function(e) {
startX = e.layerX
startY = e.layerY
startPos = { x: startX, y: startY }
posList = []
// // 得到绘制开始的坐标
// startX = e.layerX
// startY = e.layerY
// 设置鼠标已经按下
if (that.crtColor) {
flag = true
} else {
that.$message.error('没有选择元素')
}
}
// 监听鼠标按下移动
mycanvas.onmousemove = function(e) {
if (flag) {
// 得到移动的坐标
endX = e.layerX
endY = e.layerY
// 判断绘制的是什么图形
if (data_style === 'brush') {
// 创建路径
ctx.beginPath()
// 绘制线条
ctx.moveTo(startX, startY)
ctx.lineTo(endX, endY)
// 设置线条大小与颜色
ctx.strokeStyle = that.crtColor
ctx.lineWidth = that.crtLineW
// 设置线两端形状
ctx.lineCap = that.crtBrushStyle
ctx.stroke()
// 关闭路径
ctx.closePath()
// 下一次的开始位置为上一次的结束坐标
startX = endX
startY = endY
endPos = { x: endX, y: endY }
// 每隔20ms记录一个点
const now = new Date().getTime()
if (now - lastTs > 20) {
lastTs = now
posList.push(endPos)
}
}
}
}
// 监听鼠标松开与离开当前区域时间
mycanvas.onmouseup = function() {
flag = false
const obj = {
Points: [startPos, ...posList, endPos],
Type: that.crtType,
Color: that.crtColor,
Width: that.crtLineW
}
that.writeHistory(obj)
}
},
clearCanvas() {
// 得到canvas
var mycanvas = document.getElementById('canvas')
// 得到绘制对象
var ctx = mycanvas.getContext('2d')
ctx.clearRect(0, 0, 800, 500)
},
// part 2
async submit() {
var mycanvas = document.getElementById('canvas')
this.dataURL = mycanvas.toDataURL()
const params = {
image: this.dataURL,
content: this.historyArr
}
console.log(params)
await submitData(params)
const list = []
const length = Math.round(Math.random() * 5) + 3
for (let i = 0; i < length; i++) {
const colorList = [
'red',
'green',
'blue',
'skyblue',
'pink',
'black',
'gray',
'orange'
]
list.push(colorList[Math.round(Math.random() * 8)])
}
this.resList = list
},
writeHistory(obj) {
var mycanvas = document.getElementById('canvas')
obj.dataURL = mycanvas.toDataURL()
obj.timestamp = new Date().getTime()
this.historyArr.push(obj)
}
}
}
</script>
<style lang="scss" scoped>
.line {
text-align: center;
}
.item {
line-height: 1.5rem;
}
.viewport {
width: 800px;
min-height: 500px;
}
.brush-active {
border: red solid 1px;
}
.res {
width: 400px;
height: 300px;
display: inline-block;
margin-left: 20px;
margin-top: 10px;
text-align: center;
padding-top: 100px;
}
.app-container {
.resDiv{
padding: 20px 30px;
border-bottom: 1px solid #fff;
// display: flex;
// flex-wrap: wrap;
// justify-content: space-between;
}
.table{
border: 1px solid #333;
width: 100%;
padding: 10px 8px;
}
#main_frame {
margin-top: 10px;
padding: 0px 200px;
.td {
border: none;
border-left: 1px solid #333;
border-right: 1px solid #333;
}
.btn-group {
height: 550px;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 90px 0;
div {
cursor: pointer;
text-align: center;
padding: 8px 0;
font-size: 16px;
border:1px solid #fff;
border-radius: 4px;
}
}
.btn-group2 {
height: 550px;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 200px 0;
div {
cursor: pointer;
text-align: center;
padding: 8px 0;
font-size: 16px;
}
}
.center {
display: flex;
align-items: center;
flex-direction: column;
}
}
}
</style>