> 웹 프론트엔드 > H5 튜토리얼 > Canvas는 그림 낙서 기능을 구현합니다(코드 포함).

Canvas는 그림 낙서 기능을 구현합니다(코드 포함).

不言
풀어 주다: 2018-11-16 17:17:58
앞으로
5809명이 탐색했습니다.

이 글의 내용은 캔버스에 이미지 그래피티 기능을 구현하는 내용입니다(코드 포함). 도움이 필요한 친구들이 참고하시면 좋겠습니다.

Requirements

  1. 사진에 주석을 달고 내보내야 합니다.

  2. N개의 여러 장의 사진을 표시하고 최종적으로 동시에 저장해야 합니다.

  3. 은 다각형 영역 데이터(면적, 색상, 이름)에 따라 라벨을 지정해야 합니다.

해당 솔루션

  1. 캔버스를 사용하여 낙서, 원, 직사각형을 그리고 최종적으로 이미지 생성 base64 업로드는 코딩을 사용합니다

  2. 많은 수의 사진을 일괄 업로드하는 것은 시간이 많이 소요됩니다. 사용자 경험을 향상시키기 위해 원만 그리도록 변경되었습니다. 그리고 직사각형을 그리고 최종적으로 좌표로 저장합니다. 다음번에 표시되면 좌표를 기준으로 그려집니다.

  3. 폴리곤 영역의 표시는 좌표점을 기준으로 그려지며, 이름이 표시되는 위치는 폴리곤 중심입니다.

Code

<template>
  <div>
    <canvas
      :id="radom"
      :class="{canDraw: &#39;canvas&#39;}"
      :width="width"
      :height="height"
      :style="{&#39;width&#39;:`${width}px`,&#39;height&#39;:`${height}px`}"
      @mousedown="canvasDown($event)"
      @mouseup="canvasUp($event)"
      @mousemove="canvasMove($event)"
      @touchstart="canvasDown($event)"
      @touchend="canvasUp($event)"
      @touchmove="canvasMove($event)">
    </canvas>
  </div>
</template>
<script>
  // import proxy from './proxy.js'
  const uuid = require('node-uuid')
  export default {
    props: {
      canDraw: { // 图片路径
        type: Boolean,
        default: true
      },
      url: { // 图片路径
        type: String
      },
      info: { // 位置点信息
        type: Array
      },
      width: { // 绘图区域宽度
        type: String
      },
      height: { // 绘图区域高度
        type: String
      },
      lineColor: { // 画笔颜色
        type: String,
        default: 'red'
      },
      lineWidth: { // 画笔宽度
        type: Number,
        default: 2
      },
      lineType: { // 画笔类型
        type: String,
        default: 'circle'
      }
    },
    watch: {
      info (val) {
        if (val) {
          this.initDraw()
        }
      }
    },
    data () {
      return {
        // 同一页面多次渲染时,用于区分元素的id
        radom: uuid.v4(),
        // canvas对象
        context: {},
        // 是否处于绘制状态
        canvasMoveUse: false,
        // 绘制矩形和椭圆时用来保存起始点信息
        beginRec: {
          x: '',
          y: '',
          imageData: ''
        },
        // 储存坐标信息
        drawInfo: [],
        // 背景图片缓存
        img: new Image()
      }
    },
    mounted () {
      this.initDraw()
    },
    methods: {
      // 初始化绘制信息
      initDraw () {
        // 初始化画布
        const canvas = document.getElementById(this.radom)
        this.context = canvas.getContext('2d')
        // 初始化背景图片
        this.img.setAttribute('crossOrigin', 'Anonymous')
        this.img.src = this.url
        this.img.onerror = () => {
          var timeStamp = +new Date()
          this.img.src = this.url + '?' + timeStamp
        }
        this.img.onload = () => {
          this.clean()
        }
        // proxy.getBase64({imgUrl: this.url}).then((res) => {
        //   if (res.code * 1 === 0) {
        //     this.img.src = 'data:image/jpeg;base64,'+res.data
        //     this.img.onload = () => {
        //       this.clean()
        //     }
        //   }
        // })
        // 初始化画笔
        this.context.lineWidth = this.lineWidth
        this.context.strokeStyle = this.lineColor
      },
      // 鼠标按下
      canvasDown (e) {
        if (this.canDraw) {
          this.canvasMoveUse = true
          // client是基于整个页面的坐标,offset是cavas距离pictureDetail顶部以及左边的距离
          const canvasX = e.clientX - e.target.parentNode.offsetLeft
          const canvasY = e.clientY - e.target.parentNode.offsetTop
          // 记录起始点和起始状态
          this.beginRec.x = canvasX
          this.beginRec.y = canvasY
          this.beginRec.imageData = this.context.getImageData(0, 0, this.width, this.height)
          // 存储本次绘制坐标信息
          this.drawInfo.push({
            x: canvasX / this.width,
            y: canvasY / this.height,
            type: this.lineType
          })
        }
      },
      Area (p0,p1,p2) {
        let area = 0.0 ;
        area = p0.x * p1.y + p1.x * p2.y + p2.x * p0.y - p1.x * p0.y - p2.x * p1.y - p0.x * p2.y;
        return area / 2 ;
      },
      // 计算多边形质心
      getPolygonAreaCenter (points) {
        let sum_x = 0;
        let sum_y = 0;
        let sum_area = 0;
        let p1 = points[1];
        for (var i = 2; i < points.length; i++) {
          let p2 = points[i];
          let area = this.Area(points[0],p1,p2) ;
          sum_area += area ;
          sum_x += (points[0].x + p1.x + p2.x) * area;
          sum_y += (points[0].y + p1.y + p2.y) * area;
          p1 = p2 ;
        }
        return {
          x: sum_x / sum_area / 3,
          y: sum_y / sum_area / 3
        }
      },
      // 根据坐标信息绘制图形
      drawWithInfo () {
        this.info.forEach(item => {
          this.context.beginPath()
          if (!item.type) {
            // 设置颜色
            this.context.strokeStyle = item.regionColor
            this.context.fillStyle = item.regionColor
            // 绘制多边形的边
            if (typeof item.region === 'string') {
              item.region = JSON.parse(item.region)
            }
            item.region.forEach(point => {
              this.context.lineTo(point.x * this.width, point.y * this.height)
            })
            this.context.closePath()
            // 在多边形质心标注文字
            let point = this.getPolygonAreaCenter(item.region)
            this.context.fillText(item.areaName, point.x * this.width, point.y * this.height)
          } else if (item.type === 'rec') {
            this.context.rect(item.x * this.width, item.y * this.height, item.w * this.width, item.h * this.height)
          } else if (item.type === 'circle') {
            this.drawEllipse(this.context, (item.x + item.a) * this.width, (item.y + item.b) * this.height, item.a > 0 ? item.a * this.width : -item.a * this.width, item.b > 0 ? item.b * this.height : -item.b * this.height)
          }
          this.context.stroke()
        })
      },
      // 鼠标移动时绘制
      canvasMove (e) {
        if (this.canvasMoveUse && this.canDraw) {
          // client是基于整个页面的坐标,offset是cavas距离pictureDetail顶部以及左边的距离
          let canvasX = e.clientX - e.target.parentNode.offsetLeft
          let canvasY = e.clientY - e.target.parentNode.offsetTop
          if (this.lineType === 'rec') { // 绘制矩形时恢复起始点状态再重新绘制
            this.context.putImageData(this.beginRec.imageData, 0, 0)
            this.context.beginPath()
            this.context.rect(this.beginRec.x, this.beginRec.y, canvasX - this.beginRec.x, canvasY - this.beginRec.y)
            let info = this.drawInfo[this.drawInfo.length - 1]
            info.w = canvasX / this.width - info.x
            info.h = canvasY / this.height - info.y
          } else if (this.lineType === 'circle') { // 绘制椭圆时恢复起始点状态再重新绘制
            this.context.putImageData(this.beginRec.imageData, 0, 0)
            this.context.beginPath()
            let a = (canvasX - this.beginRec.x) / 2
            let b = (canvasY - this.beginRec.y) / 2
            this.drawEllipse(this.context, this.beginRec.x + a, this.beginRec.y + b, a > 0 ? a : -a, b > 0 ? b : -b)
            let info = this.drawInfo[this.drawInfo.length - 1]
            info.a = a / this.width
            info.b = b / this.height
          }
          this.context.stroke()
        }
      },
      // 绘制椭圆
      drawEllipse (context, x, y, a, b) {
        context.save()
        var r = (a > b) ? a : b
        var ratioX = a / r
        var ratioY = b / r
        context.scale(ratioX, ratioY)
        context.beginPath()
        context.arc(x / ratioX, y / ratioY, r, 0, 2 * Math.PI, false)
        context.closePath()
        context.restore()
      },
      // 鼠标抬起
      canvasUp (e) {
        if (this.canDraw) {
          this.canvasMoveUse = false
        }
      },
      // 获取坐标信息
      getInfo () {
        return this.drawInfo
      },
      // 清空画布
      clean () {
        this.context.drawImage(this.img, 0, 0, this.width, this.height)
        this.drawInfo = []
        if (this.info && this.info.length !== 0) this.drawWithInfo()
      }
    }
  }
</script>
<style scoped>
  .canvas{
    cursor: crosshair;
  }
</style>
로그인 후 복사

매개변수로 전달해야 함

    #🎜🎜 #
  • 그림 경로

url: string
로그인 후 복사
  • 도면 영역 너비

width: string
로그인 후 복사
#🎜 🎜#
    도면 영역 높이
  • height: string
    로그인 후 복사
전달된 매개변수 선택

#🎜🎜 #

그릴 수 있는지 여부는 기본값이 true입니다
  • canDraw: boolean
    로그인 후 복사
    로그인 후 복사
좌표점 정보는 표시되지 않습니다. 전달되지 않으면 그려집니다
  • info: string
    로그인 후 복사
드로어블인지 여부에 관계없이 기본값은 true입니다
  • canDraw: boolean
    로그인 후 복사
    로그인 후 복사
    #🎜 🎜#
  • 그리기 색상, 기본 빨간색
  • lineColor: string
    로그인 후 복사

  • 그리기 펜 너비 , 기본 2
  • lineWidth: number
    로그인 후 복사

  • 드로잉 펜 유형, Rec, Circle, 기본 Rec
  • lineType: string
    로그인 후 복사

    메소드 호출 가능

캔버스 지우기

  • clean()
    로그인 후 복사
    #🎜 🎜#

    좌표 정보 반환
    getInfo()
    로그인 후 복사
  • 특별 지침

  • # 🎜🎜#

    canvas 객체를 얻을 수 없습니다. 좌표는 상위 요소의 좌표를 통해 얻어지므로 구성 요소의 상위 요소 위 수준에서 너무 많은 위치 지정이나 중첩이 있어서는 안 됩니다. 그렇지 않으면 그리기 좌표가 오프셋.

      도메인 이름이 다른 이미지에는 도메인 간 문제가 있을 수 있습니다. 많은 정보를 읽었지만 최종 프로젝트에서는 다음을 사용했습니다. 이미지를 base64로 변환하는 노드 서비스입니다. 인터페이스는 캔버스에 그려서 해결됩니다. 다른 프로젝트에는 적용되지 않을 수 있습니다. 더 나은 솔루션이 있으면 공유해 주세요.
    • 좌표점 데이터 내보내기는 일반 패턴의 좌표점만 내보낼 수 있습니다. 무작위로 낙서하면 너무 많은 좌표점이 무너지기 때문입니다(어느 정도 시도하지는 않았지만). 발생합니다) 충돌), 고성능 구현이 있으면 공유해 주세요.
    • 그래피티를 저장한 후 이미지 url을 요청했는데도 요청이 안되는 경우는 CDN 캐싱 문제로, 임의의 철자를 써서 해결할 수 있습니다. 이미지 경로 뒤의 코드.

    위 내용은 Canvas는 그림 낙서 기능을 구현합니다(코드 포함).의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    관련 라벨:
    원천:segmentfault.com
    본 웹사이트의 성명
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
    인기 튜토리얼
    더>
    최신 다운로드
    더>
    웹 효과
    웹사이트 소스 코드
    웹사이트 자료
    프론트엔드 템플릿