ホームページ ウェブフロントエンド H5 チュートリアル Canvasに絵の落書き機能を実装(コード付き)

Canvasに絵の落書き機能を実装(コード付き)

Nov 16, 2018 pm 05:17 PM
canvas javascript vue.js

この記事の内容は、キャンバス上での画像落書き機能の実現に関するものです。必要な方は参考にしていただければ幸いです。

要件

  1. 画像に注釈を付けてエクスポートする必要があります。

  2. N 個の複数の写真にマークを付け、最終的に同時に保存する必要があります。

  3. ポリゴン領域データ (領域、色、名前) に基づいてラベルを付ける必要があります。

#対応する解決策

  1. キャンバスを使用して落書き、円、四角形を描画し、最後にアップロード用の画像 Base64 エンコードを生成します

  2. 大量の写真を一括アップロードするのは時間がかかります。ユーザー エクスペリエンスを向上させるために、円と四角形の描画のみを実装し、最後にそれらを座標として保存し、その座標に従って描画します。次回表示されるとき。

  3. ポリゴン領域の表示は座標点に基づいて描画され、名前が表示される位置がポリゴンの重心となります。

  4. #コード
<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()
    ログイン後にコピー

    特別な指示
  • キャンバス オブジェクトは、親要素の座標を通じて座標を取得できません。したがって、レベルでの配置とネストが多すぎることはできません。コンポーネントの親要素の上にある場合、描画座標はオフセットされます。

異なるドメイン名を持つ画像にはクロスドメインの問題が発生する可能性があります。多くの情報を読みましたが、最終的なプロジェクトではノード サービスを使用して作成しました。画像をbase64に変換し、キャンバス上に描画することで解決します。他のプロジェクトには当てはまらない可能性がありますので、より良い解決策があれば共有してください。

    座標点データのエクスポートでは、ランダムに落書きされた座標点が多すぎると崩れてしまうため、規則的なパターンの座標点しかエクスポートできません(どの程度までは試していませんが)パフォーマンスの高い実装方法がある場合は、共有してください。
  • 落書きを保存してから画像の URL をリクエストしてもリクエストができない場合は、CDN キャッシュの問題が原因です。これは、グラフィティの後にランダムなコードを追加することで解決できます。画像のパス。

以上がCanvasに絵の落書き機能を実装(コード付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 簡単な JavaScript チュートリアル: HTTP ステータス コードを取得する方法 Jan 05, 2024 pm 06:08 PM

JavaScript チュートリアル: HTTP ステータス コードを取得する方法、特定のコード例が必要です 序文: Web 開発では、サーバーとのデータ対話が頻繁に発生します。サーバーと通信するとき、多くの場合、返された HTTP ステータス コードを取得して操作が成功したかどうかを判断し、さまざまなステータス コードに基づいて対応する処理を実行する必要があります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法を説明し、いくつかの実用的なコード例を示します。 XMLHttpRequestの使用

キャンバス フレームワークを学び、一般的に使用されるキャンバス フレームワークについて詳しく説明します キャンバス フレームワークを学び、一般的に使用されるキャンバス フレームワークについて詳しく説明します Jan 17, 2024 am 11:03 AM

Canvas フレームワークを探索する: 一般的に使用される Canvas フレームワークを理解するには、特定のコード例が必要です。 はじめに: Canvas は HTML5 で提供される描画 API であり、これを通じて豊富なグラフィックスやアニメーション効果を実現できます。描画の効率と利便性を向上させるために、多くの開発者がさまざまな Canvas フレームワークを開発しました。この記事では、一般的に使用される Canvas フレームワークをいくつか紹介し、読者がこれらのフレームワークの使用方法をより深く理解できるように、具体的なコード例を示します。 1.EaselJSフレームワークEa

JavaScript で HTTP ステータス コードを簡単に取得する方法 JavaScript で HTTP ステータス コードを簡単に取得する方法 Jan 05, 2024 pm 01:37 PM

JavaScript で HTTP ステータス コードを取得する方法の紹介: フロントエンド開発では、バックエンド インターフェイスとの対話を処理する必要があることが多く、HTTP ステータス コードはその非常に重要な部分です。 HTTP ステータス コードを理解して取得すると、インターフェイスから返されたデータをより適切に処理できるようになります。この記事では、JavaScript を使用して HTTP ステータス コードを取得する方法と、具体的なコード例を紹介します。 1. HTTP ステータス コードとは何ですか? HTTP ステータス コードとは、ブラウザがサーバーへのリクエストを開始したときに、サービスが

ゲーム開発におけるキャンバスの強力な役割と応用を探る ゲーム開発におけるキャンバスの強力な役割と応用を探る Jan 17, 2024 am 11:00 AM

ゲーム開発におけるキャンバスの力と応用を理解する 概要: インターネット技術の急速な発展に伴い、Web ゲームはプレイヤーの間でますます人気が高まっています。 Web ゲーム開発の重要な部分として、キャンバス テクノロジーがゲーム開発に徐々に登場し、その強力なパワーと応用性を示しています。この記事では、ゲーム開発におけるキャンバスの可能性を紹介し、具体的なコード例を通じてその応用例を示します。 1. Canvas テクノロジの概要 Canvas は HTML5 の新しい要素で、これにより次のことが可能になります。

キャンバスコードを記述する場所 キャンバスコードを記述する場所 Dec 20, 2023 pm 03:17 PM

Canvas コードは、HTML ファイルの <body> タグ内に、通常は HTML ドキュメントの一部として記述できます。Canvas コードの核心は、Canvas 要素のコンテキストを取得して操作することです。Canvas 要素への参照が取得されます。 document.getElementById('myCanvas') を通じて、 getContext('2d') を使用して 2D 描画コンテキストを取得します。

Vue.js vs. React:プロジェクト固有の考慮事項 Vue.js vs. React:プロジェクト固有の考慮事項 Apr 09, 2025 am 12:01 AM

VUE.JSは、中小規模のプロジェクトや迅速な反復に適していますが、Reactは大規模で複雑なアプリケーションに適しています。 1)Vue.jsは使いやすく、チームが不十分な状況やプロジェクトスケールが小さい状況に適しています。 2)Reactにはより豊富なエコシステムがあり、高性能で複雑な機能的ニーズを持つプロジェクトに適しています。

Vue.jsは学ぶのが難しいですか? Vue.jsは学ぶのが難しいですか? Apr 04, 2025 am 12:02 AM

Vue.jsは、特にJavaScriptファンデーションを持つ開発者にとって、学ぶのは難しくありません。 1)その進歩的な設計とレスポンシブシステムは、開発プロセスを簡素化します。 2)コンポーネントベースの開発により、コード管理がより効率的になります。 3)使用例は、基本的および高度な使用法を示しています。 4)一般的なエラーは、vuedevtoolsを介してデバッグできます。 5)V-IF/V-Showや重要な属性を使用するなど、パフォーマンスの最適化とベストプラクティスは、アプリケーションの効率を向上させることができます。

中国教育分野におけるCanvasの発展動向と将来展望 中国教育分野におけるCanvasの発展動向と将来展望 Jan 17, 2024 am 10:22 AM

科学技術の急速な発展と教育分野における情報技術の広範な応用に伴い、Canvas は世界をリードするオンライン学習管理システムとして、中国の教育業界で徐々に台頭してきています。 Canvas の登場は、中国の教育と指導方法の改革に新たな可能性をもたらします。この記事では、中国の教育分野におけるCanvasの開発傾向と展望について探っていきます。まず第一に、中国の教育分野における Canvas の開発トレンドの 1 つは、徹底した統合です。クラウド コンピューティング、ビッグ データ、人工知能の急速な発展により、Canvas はますます

See all articles