【canvas】canvas笔刷brush

906 阅读1分钟

背景

这是这周帮其他中心搞的项目,很有意思的一个项目。
输入,canvas随笔画,传入给服务端图片和一些操作历史数据。
服务端根据训练模型,人工智能,渲染生成一些效果图。
等于就是简单画几笔,让你也能画出牛逼的画。

需求

也很简单,给了我一张示意图。

image.png

那就开干呗,把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>