WeChatアプレットプロジェクトでは、開発モジュールに手書き署名機能が含まれ、WeChatアプレットキャンバスがデビューします 序文 WeChatアプレットキャンバスが署名機能を実装 コアコンテンツの紹介: (1)署名の実施、開始、移動、終了 (2)書き直し (3)完了 (4)アップロード 1. WeChatアプレットキャンバスが署名機能を実装効果のデモンストレーション: (1)署名の実施 (2)書き直し (3)完了 完了したら、対応する位置に画像を表示します (4)ビジネスニーズに応じて、背景に写真をアップロードし、必要な場所に表示することができます。 2. コード1. すべてのデモ wxml <!--pages/canvas-test/canvas-test.wxml--> <view class="handCenter"> <canvas class="handWriting" disabled-scroll="true" bindtouchstart="uploadScaleStart" bindtouchmove="uploadScaleMove" bindtouchend="uploadScaleEnd" bindtap="mouseDown" canvas-id="handWriting"> </キャンバス> </ビュー> <view class="handBtn"> <button catchtap="retDraw" class="delBtn">書き換え</button> <button catchtap="subCanvas" class="subBtn">完了</button> </ビュー> <view class="preview"> <画像 wx:if="{{tmpPath}}" style="width:100%;height:100%;" src="{{tmpPath}}"></画像> </ビュー> js const アプリ = getApp() const api = require('../../utils/request.js'); //相対パス const apiEev = require('../../config/config'); ページ({ データ: { キャンバス名: '手書き', ctx: ''、 キャンバス幅: 0, キャンバスの高さ: 0, transparent: 1, // 透明度 selectColor: 'black', lineColor: '#1A1A1A', // 色 lineSize: 1.5, // 複数行に注意してください lineMin: 0.5, // 最小ストローク半径 lineMax: 4, // 最大ストローク半径 pressure: 1, // デフォルトの圧力 Smoothness: 60, // 滑らかさ、速度を計算するために 60 距離を使用します currentPoint: {}, currentLine: [], // 現在の行 firstTouch: true, // 最初のトリガー radius: 1, // 円の半径 cutArea: { top: 0, right: 0, bottom: 0, left: 0 }, // 切り取り領域 bethelPoint: [], // すべての行によって生成されたベジェ点を保存します。 最終ポイント: 0, chirography: [], //handwritingcurrentChirography: {}, //現在の手書きlinePrack: [], //線の軌跡、生成された線の実際のポイントtmpPath:'' }, // 手書き開始 uploadScaleStart (e) { if (e.type != 'touchstart') は false を返します。 ctx = this.data.ctx; とします。 ctx.setFillStyle(this.data.lineColor); // 初期の線の色の設定 ctx.setGlobalAlpha(this.data.transparent); // 半透明度の設定 let currentPoint = { x: e.touches[0].x、 y: e.touches[0].y } currentLine を this.data.currentLine とします。 現在の行をシフト解除({ 時間: 新しいDate().getTime()、 否定: 0, x: 現在のポイント.x、 y: 現在のポイント.y }) this.setData({ 現在のポイント、 // 現在の行 }) if (this.data.firstTouch) { this.setData({ カットエリア: { 上: currentPoint.y、右: currentPoint.x、下: currentPoint.y、左: currentPoint.x }, ファーストタッチ: 偽 }) } this.pointToLine(現在の行); }, // 手書きの動き uploadScaleMove(e) { e.type != 'touchmove' の場合は false を返します。 if (e.cancelable) { // デフォルトの動作が無効になっているかどうかを確認します if (!e.defaultPrevented) { e.preventDefault(); } } ポイント = { x: e.touches[0].x、 y: e.touches[0].y } //カットをテストする if (point.y < this.data.cutArea.top) { this.data.cutArea.top = point.y; } point.y < 0 の場合、this.data.cutArea.top = 0; (ポイントx>this.data.cutArea.right)の場合{ this.data.cutArea.right = point.x; } if (this.data.canvasWidth - point.x <= 0) { this.data.cutArea.right = this.data.canvasWidth; } (ポイントy>this.data.cutArea.bottom)の場合{ this.data.cutArea.bottom = point.y; } if (this.data.canvasHeight - point.y <= 0) { this.data.cutArea.bottom = this.data.canvasHeight; } (ポイントxがthis.data.cutArea.leftの場合){ this.data.cutArea.left = point.x; } point.x < 0 の場合、this.data.cutArea.left = 0; this.setData({ 最後のポイント: this.data.currentPoint、 currentPoint: ポイント }) currentLine = this.data.currentLine とします。 現在の行をシフト解除({ 時間: 新しいDate().getTime()、 dis: this.distance(this.data.currentPoint, this.data.lastPoint), x: ポイント.x, y: ポイント.y }) // this.setData({ // 現在の行 // }) this.pointToLine(現在の行); }, // 手書きが終わる uploadScaleEnd (e) { e.type != 'touchend' の場合、0 を返します。 ポイント = { x: e.changedTouches[0].x、 y: e.changedTouches[0].y } this.setData({ 最後のポイント: this.data.currentPoint、 currentPoint: ポイント }) currentLine = this.data.currentLine とします。 現在の行をシフト解除({ 時間: 新しいDate().getTime()、 dis: this.distance(this.data.currentPoint, this.data.lastPoint), x: ポイント.x, y: ポイント.y }) // this.setData({ // 現在の行 // }) (現在の行の長さ > 2) の場合 { var info = (currentLine[0].time - currentLine[currentLine.length - 1].time) / currentLine.length; //$("#info").text(info.toFixed(2)); } // ストロークが完了したら、手書きの座標点を保存し、クリアして、現在の手書きが手書き領域内にあるかどうかを確認するチェックを追加します。 this.pointToLine(現在の行); var 現在の書体 = { 行サイズ: this.data.lineSize、 線の色: this.data.lineColor }; var chirography = this.data.chirography chirography.unshift(現在のCirography); this.setData({ 筆跡 }) var linePrack = this.data.linePrack linePrack.unshift(this.data.currentLine); this.setData({ ラインプラック、 現在の行: [] }) }, オンロード(){ キャンバス名を this.data.canvasName とします ctx = wx.createCanvasContext(キャンバス名) とします。 this.setData({ ctx: ctx }) var クエリ = wx.createSelectorQuery(); クエリ.select('.handCenter').boundingClientRect(rect => { this.setData({ キャンバス幅: rect.width、 キャンバスの高さ: rect.height }) }).exec(); }, サブキャンバス(){ // let that = this を追加します ctx = this.data.ctx; とします。 ctx.draw(true,setTimeout(function(){ //新しいタイマーとコールバック wx.canvasToTempFilePath({ x: 0, y: 0, 幅: 375, 高さ: 152, キャンバスID: '手書き', ファイルタイプ: 'png', 成功: function(res) { that.setData({ tmpPath:res.tempファイルパス }) console.log(that.data.tmpPath,'それが何であるかを確認してください') that.upImgs(that.data.tmpPath,0) } }, ctx) },1000)) }, // 保存した画像をファイルサーバーにアップロードするためのパスを追加します upImgs: function (imgurl, index) { console.log(imgurl,'パスを確認してください') var that = this; wx.uploadFile({ url: apiEev.api + 'xxxx', //バックグラウンドアップロードパス filePath: imgurl, 名前: 'ファイル', ヘッダー: { 'コンテンツタイプ': 'マルチパート/フォームデータ' }, フォームデータ: null、 成功: 関数 (res) { console.log(res) //インターフェースはネットワークパスを返します var data = JSON.parse(res.data) console.log(data,'データが何であるかを確認します') if (data.code == "成功") { console.log('成功') } } }) }, retDraw() { this.data.ctx.clearRect(0, 0, 700, 730) this.data.ctx.draw() this.setData({ tmpPath:'' }) }, // 2 つの点の間に線を描画します。パラメータは line で、最も近い 2 つの開始点を描画します。 pointToLine (線) { this.calcBethelLine(行); 戻る; }, //補間方法を計算し、 calcBethelLine (行) { (行の長さ<= 1)の場合{ 行[0].r = this.data.radius; 戻る; } x0、x1、x2、y0、y1、y2、r0、r1、r2、len、lastRadius、dis = 0、time = 0、curveValue = 0.5 とします。 行の長さが2以下の場合 x0 = 行[1].x y0 = 行[1].y x2 = 線[1].x + (線[0].x - 線[1].x) * 曲線値; y2 = 線[1].y + (線[0].y - 線[1].y) * 曲線値; //x2 = 行[1].x; //y2 = 行[1].y; x1 = x0 + (x2 - x0) * 曲線値; y1 = y0 + (y2 - y0) * 曲線値;; } それ以外 { x0 = 線[2].x + (線[1].x - 線[2].x) * 曲線値; y0 = 線[2].y + (線[1].y - 線[2].y) * 曲線値; x1 = 行[1].x; y1 = 行[1].y; x2 = x1 + (line[0].x - x1) * 曲線値; y2 = y1 + (line[0].y - y1) * 曲線値; } // 計算式から、3 つの点は (x0, y0)、(x1, y1)、(x2, y2) です。(x1, y1) は制御点であり、曲線上には配置されません。実際、この点は手書きで取得した実際の点でもありますが、曲線上に配置されます。len = this.distance({ x: x2, y: y2 }, { x: x0, y: y0 }); 最後の半径 = this.data.radius; (n = 0; n < 行の長さ - 1; n++) の場合 { dis += 行[n].dis; 時間 += 行[n].時間 - 行[n + 1].時間; if (dis > this.data.smoothness) break; } this.setData({ 半径: Math.min(time / len * this.data.pressure + this.data.lineMin, this.data.lineMax) * this.data.lineSize }); 行[0].r = this.data.radius; //手書きの半径を計算します。 行の長さが2以下の場合 r0 = (lastRadius + this.data.radius) / 2; r1 = r0; r2 = r1; //戻る; } それ以外 { r0 = (行[2].r + 行[1].r) / 2; r1 = 行[1].r; r2 = (行[1].r + 行[0].r) / 2; } n = 5 とします。 ポイントを [] とします。 (i = 0; i < n; i++ とします) { t = i / (n - 1)とします。 x = (1 - t) * (1 - t) * x0 + 2 * t * (1 - t) * x1 + t * t * x2 とします。 y = (1 - t) * (1 - t) * y0 + 2 * t * (1 - t) * y1 + t * t * y2 とします。 r = lastRadius + (this.data.radius - lastRadius) / n * i とします。 point.push({ x: x, y: y, r: r }); ポイントの長さが3の場合 a = this.ctaCalc(point[0].x, point[0].y, point[0].r, point[1].x, point[1].y, point[1].r, point[2].x, point[2].y, point[2].r);とします。 a[0].color = this.data.lineColor; // bethelPoint を this.data.bethelPoint とします。 // コンソール.log(a) // コンソールログ(this.data.bethelPoint) // bethelPoint = bethelPoint.push(a); 描画する 点 = [{ x: x, y: y, r: r }]; } } this.setData({ 現在の行: 行 }) }, // 2点間の距離を求める distance (a, b) { x = bx - ax とします。 y = by - ayとします。 Math.sqrt(x * x + y * y) を返します。 }, ctaCalc (x0, y0, r0, x1, y1, r1, x2, y2, r2) { a = [], vx01, vy01, norm, n_x0, n_y0, vx21, vy21, n_x2, n_y2とします。 関数x1とx0の積。 vy01 = y1 - y0; ノルム = Math.sqrt(vx01 * vx01 + vy01 * vy01 + 0.0001) * 2; vx01 = vx01 / ノルム * r0; vy01 = vy01 / ノルム * r0; 0x0 ... y0 = -vx01; を出力します。 vx21 = x1 - x2; vy21 = y1 - y2; ノルム = Math.sqrt(vx21 * vx21 + vy21 * vy21 + 0.0001) * 2; vx21 = vx21 / ノルム * r2; vy21 = vy21 / ノルム * r2; 関数 n_x2 = -vy21; y2 = vx21; のようになります。 a.push({ mx: x0 + n_x0, my: y0 + n_y0, 色: "#1A1A1A" }); a.push({ c1x: x1 + n_x0, c1y: y1 + n_y0, c2x: x1 + n_x2, c2y: y1 + n_y2, ex: x2 + n_x2, ey: y2 + n_y2 }); a.push({ c1x: x2 + n_x2 - vx21, c1y: y2 + n_y2 - vy21, c2x: x2 - n_x2 - vx21, c2y: y2 - n_y2 - vy21, ex: x2 - n_x2, ey: y2 - n_y2 }); a.push({ c1x: x1 - n_x2, c1y: y1 - n_y2, c2x: x1 - n_x0, c2y: y1 - n_y0, ex: x0 - n_x0, ey: y0 - n_y0 }); a.push({ c1x: x0 - n_x0 - vx01, c1y: y0 - n_y0 - vy01, c2x: x0 + n_x0 - vx01, c2y: y0 + n_y0 - vy01, ex: x0 + n_x0, ey: y0 + n_y0 }); 1. 固定長のmxを0にする。 文字列をparse(0)します。 a[0].my = a[0].my.toFixed(1); a[0].my = parseFloat(a[0].my); (i = 1; i < a.length; i++ とします) { 1. 固定長のc1xを固定長のc1xに変換する。 パースフロート 1. 固定長のc1yを出力します。 a[i].c1y = parseFloat(a[i].c1y); 1. 固定長のc2xをa[i]に代入する。 パースフロート 1. 固定長のc2yを出力します。 python 4.0 では、 ... a[i].ex = a[i].ex.toFixed(1); パースフロート a[i].ey = a[i].ey.toFixed(1); a[i].ey = parseFloat(a[i].ey); } を返します。 }, bethelDraw (ポイント、is_fill、色) { // let that = this を追加します ctx = this.data.ctx; とします。 ctx.beginPath(); ctx.moveTo(ポイント[0].mx、ポイント[0].my); (未定義!=色)の場合{ ctx.setFillStyle(色); ctx.setStrokeStyle(色); } それ以外 { ctx.setFillStyle(ポイント[0].color); ctx.setStrokeStyle(ポイント[0].color); } (i = 1 とします; i < point.length; i++) { ctx.bezierCurveTo(ポイント[i].c1x、ポイント[i].c1y、ポイント[i].c2x、ポイント[i].c2y、ポイント[i].ex、ポイント[i].ey); } ctx.stroke(); (未定義!= is_fill)の場合{ ctx.fill(); // グラフィックを塗りつぶします (後で描画したグラフィックは前のグラフィックを覆います。描画するときは順序に注意してください) } ctx.draw(true) }, selectColorEvent (イベント) { console.log(イベント) var color = event.currentTarget.dataset.colorValue; var colorSelected = event.currentTarget.dataset.color; this.setData({ 選択色: 選択された色、 線の色: 色 }) } }) /* ページ/canvas-test2/canvas-test2.wxss */ .キャンバスID { 位置: 絶対; 左: 50%; 上: 0; 変換: 翻訳(-50%); zインデックス: 1; 境界線: 2px 破線 #ccc; 境界線の半径: 8px; 下部マージン: 66px; } .handCenter{ 境界線: 1px 実線の赤; } .手書き{ 幅: 100%; } .プレビュー{ 幅: 375ピクセル; 高さ: 152px; } 2. 主要部品の分析 (1)署名、開始、移動、終了の基本実装 // 手書き開始 uploadScaleStart (e) { if (e.type != 'touchstart') は false を返します。 ctx = this.data.ctx; とします。 ctx.setFillStyle(this.data.lineColor); // 初期の線の色の設定 ctx.setGlobalAlpha(this.data.transparent); // 半透明度の設定 let currentPoint = { x: e.touches[0].x、 y: e.touches[0].y } currentLine を this.data.currentLine とします。 現在の行をシフト解除({ 時間: 新しいDate().getTime()、 否定: 0, x: 現在のポイント.x、 y: 現在のポイント.y }) this.setData({ 現在のポイント、 // 現在の行 }) if (this.data.firstTouch) { this.setData({ カットエリア: { 上: currentPoint.y、右: currentPoint.x、下: currentPoint.y、左: currentPoint.x }, ファーストタッチ: 偽 }) } this.pointToLine(現在の行); }, // 手書きの動き uploadScaleMove(e) { e.type != 'touchmove' の場合は false を返します。 if (e.cancelable) { // デフォルトの動作が無効になっているかどうかを確認します if (!e.defaultPrevented) { e.preventDefault(); } } ポイント = { x: e.touches[0].x、 y: e.touches[0].y } //カットをテストする if (point.y < this.data.cutArea.top) { this.data.cutArea.top = point.y; } point.y < 0 の場合、this.data.cutArea.top = 0; (ポイントx>this.data.cutArea.right)の場合{ this.data.cutArea.right = point.x; } if (this.data.canvasWidth - point.x <= 0) { this.data.cutArea.right = this.data.canvasWidth; } (ポイントy>this.data.cutArea.bottom)の場合{ this.data.cutArea.bottom = point.y; } if (this.data.canvasHeight - point.y <= 0) { this.data.cutArea.bottom = this.data.canvasHeight; } (ポイントxがthis.data.cutArea.leftの場合){ this.data.cutArea.left = point.x; } point.x < 0 の場合、this.data.cutArea.left = 0; this.setData({ 最後のポイント: this.data.currentPoint、 currentPoint: ポイント }) currentLine = this.data.currentLine とします。 現在の行をシフト解除({ 時間: 新しいDate().getTime()、 dis: this.distance(this.data.currentPoint, this.data.lastPoint), x: ポイント.x, y: ポイント.y }) // this.setData({ // 現在の行 // }) this.pointToLine(現在の行); }, // 手書きが終わる uploadScaleEnd (e) { e.type != 'touchend' の場合、0 を返します。 ポイント = { x: e.changedTouches[0].x、 y: e.changedTouches[0].y } this.setData({ 最後のポイント: this.data.currentPoint、 currentPoint: ポイント }) currentLine = this.data.currentLine とします。 現在の行をシフト解除({ 時間: 新しいDate().getTime()、 dis: this.distance(this.data.currentPoint, this.data.lastPoint), x: ポイント.x, y: ポイント.y }) // this.setData({ // 現在の行 // }) (現在の行の長さ > 2) の場合 { var info = (currentLine[0].time - currentLine[currentLine.length - 1].time) / currentLine.length; //$("#info").text(info.toFixed(2)); } // ストロークが完了したら、手書きの座標点を保存し、クリアして、現在の手書きが手書き領域内にあるかどうかを確認するチェックを追加します。 this.pointToLine(現在の行); var 現在の書体 = { 行サイズ: this.data.lineSize、 線の色: this.data.lineColor }; var chirography = this.data.chirography chirography.unshift(現在のCirography); this.setData({ 筆跡 }) var linePrack = this.data.linePrack linePrack.unshift(this.data.currentLine); this.setData({ ラインプラック、 現在の行: [] }) }, 最初にonloadで初期化することを忘れないでください コードを取得して直接使用する (2)再署名 簡単に言えば、キャンバスをクリアすることを意味します。 retDraw() { this.data.ctx.clearRect(0, 0, 700, 730) this.data.ctx.draw() this.setData({ tmpPath:'' }) }, (3)署名完了 サブキャンバス(){ // let that = this を追加します ctx = this.data.ctx; とします。 ctx.draw(true,setTimeout(function(){ //新しいタイマーとコールバック wx.canvasToTempFilePath({ x: 0, y: 0, 幅: 375, 高さ: 152, キャンバスID: '手書き', ファイルタイプ: 'png', 成功: function(res) { that.setData({ tmpPath:res.tempファイルパス }) console.log(that.data.tmpPath,'それが何であるかを確認してください') that.upImgs(that.data.tmpPath,0) } }, ctx) },1000)) }, 内部のコールバックの方が重要です:
(4)ビジネスニーズに応じて、背景に写真をアップロードし、必要な場所に表示することができます。 重要なのはバックグラウンドにアップロードする方法です // 保存した画像をファイルサーバーにアップロードするためのパスを追加します upImgs: function (imgurl, index) { console.log(imgurl,'パスを確認してください') var that = this; wx.uploadFile({ url: apiEev.api + 'xxxx', //バックグラウンドファイルアップロードパスインターフェース filePath: imgurl, 名前: 'ファイル', ヘッダー: { 'コンテンツタイプ': 'マルチパート/フォームデータ' }, フォームデータ: null、 成功: 関数 (res) { console.log(res) //インターフェースはネットワークパスを返します var data = JSON.parse(res.data) console.log(data,'データが何であるかを確認します') if (data.code == "成功") { console.log('成功') } } }) }, 要約する WeChat ミニプログラム キャンバスは署名機能を実装します。 特別な注意:実機デバッグおよび体験版では遅延が発生する可能性があります。可能であれば、プレリリース版にリリースして、パフォーマンスに影響があるかどうかを確認してください。 以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。 以下もご興味があるかもしれません:
|
<<: Mysql 5.7.17 をインストールした後、MySQL にログインするチュートリアル
>>: CentOS7 ファイアウォールとオープンポートの簡単な使い方の簡単な紹介
この記事では、CSS無限シームレススクロール効果を実現するためのvue3の具体的なコードを参考までに...
始める段階から初心者になるまで、Linux オペレーティング システムは不可欠です。最初のステップは...
目次Vue でのモデルバインド表示の if の v-text の説明v-html: v-オンv-if...
目次1. テレポートの使用2. モーダルダイアログコンポーネントを完成させる3. コンポーネントのレ...
01 並列レプリケーションの概念MySQL のマスター スレーブ レプリケーション アーキテクチャで...
この記事では、ローカル yum ソースを使用して CentOS 上に LAMP 環境を構築する方法に...
突然、ドキュメントの保存と共同作業のためのプライベート サービスを構築する必要がありました。多くの場...
NFS とは何ですか?ネットワークファイルシステムネットワーク上でファイルを保存および整理するための...
【著者】 Liu Bo: Ctrip テクニカル サポート センターのシニア データベース マネージ...
Jmeter がネイティブの結果表示機能を提供していることは誰もが知っています。ネイティブの結果表示...
目次序文1. データベースの基礎知識1. データベースとは何ですか? 2. データベースの分類3. ...
目次1. 本来の定義2. JS操作、幅の変更を例に3. 効果: 幅が変更されました 1. 本来の定義...
HTMLに触れた当初はレイアウトにいつもテーブルを使っていましたが、とても面倒で見た目も悪かったの...
1. はじめに● ランダム書き込みではヘッドがトラックを頻繁に変更するため、効率が大幅に低下します。...
残念ながら、社内の IM のテスト中に MYSQL_DATA_TRUNCATED エラーが再び発生し...