最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript使用canvas實(shí)現(xiàn)手寫簽名功能

 更新時間:2023年08月22日 14:04:10   作者:xintianyou  
最近遇到一個h5手寫簽名的需求,按理說這種功能網(wǎng)上隨便一搜一大把現(xiàn)成的源碼和組件,但是像這種比較經(jīng)典又很簡單的功能,還是要弄清楚到底怎么實(shí)現(xiàn)的,所以接下來本文就給大家介紹一下如何用canvas實(shí)現(xiàn)手寫簽名功能

預(yù)覽效果

如果不想閱讀文章,可直接查看源碼

先實(shí)現(xiàn)基本需求(能簽名即可)

整理思路

  • 準(zhǔn)備一個canvas畫布,得到context對象

  • 指定畫筆的樣式

  • 監(jiān)聽鼠標(biāo) / 手指的移動,得到每一次移動在畫布上的坐標(biāo)點(diǎn),記錄下來

  • 將這些點(diǎn)繪制到畫布上形成線條

<canvas?width="600"?height="400"?id="canvas"?style="background-color:?#ddd;"></canvas>

為了方便調(diào)試,我們本次僅演示pc端的操作。移動端思路是一樣的,只不過監(jiān)聽的API不同。

常見的操作方式是:當(dāng)鼠標(biāo)左鍵按下的時候在畫布上移動鼠標(biāo),就可以繪制。沒有按下鼠標(biāo)時,不管它。

注釋很重要,我盡量寫得很詳細(xì)

window.onload?=?function?()?{????
    //?默認(rèn)鼠標(biāo)是沒有按下的????
    let?isDown?=?false;????
    //?記錄上一次鼠標(biāo)的位置????
    let?lastX?=?0;?//?x軸????
    let?lastY?=?0;?//?y軸????????
    //?獲取canvas
  ??const?canvas?=?document.getElementById("canvas");????
    //?獲取canvas的上下文????
    const?ctx?=?canvas.getContext("2d");????
    //?定義線條的寬度,即畫筆的粗細(xì)????
    ctx.lineWidth?=?3;????
    //?定義畫筆的顏色????
    ctx.strokeStyle?=?"#000";????
    /**
??????* 定義繪制方法
????? *?線條其實(shí)是由兩個點(diǎn)連起來的一個線段
????? * 一個又一個的小線段,連起來就組成了一個線條
??????*?在畫布上繪制線條,主要用到的三個核心方法??????
      * moveTo:?是 Canvas 2D API 將一個新的子路徑的起始點(diǎn)移動到?(x,y)?坐標(biāo)的法。???
      * lineTo:?是 Canvas 2D API 使用直線連接子路徑的終點(diǎn)到 x,y 坐標(biāo)的法。??????
      *?當(dāng)然,定義了起點(diǎn)和終點(diǎn)還不夠,還需要手動調(diào)用開始繪制這個路徑
? ? ? * startX 和 startY 一起組成了起點(diǎn)的坐標(biāo)
????? * endX?和 endY?一起組成了線段終點(diǎn)的坐標(biāo)
??????*/
????function?draw(startX, startY, endX, endY)?{????????
        //?起點(diǎn)????????
        ctx.moveTo(startX, startY);????????
        //?終點(diǎn)????????
        ctx.lineTo(endX, endY);????????
        // 調(diào)用 stroke,即可看到繪制的線條????????
        ctx.stroke();????
    }????
    //?監(jiān)聽鼠標(biāo)按下,得到按下時鼠標(biāo)在畫布上的坐標(biāo)????
    canvas.addEventListener("mousedown",?({?x,?y?})?=>?{
        isDown?=?true;
        // 按下時的點(diǎn)作為起點(diǎn)
        lastX?=?x;
        lastY?=?y;
        //?創(chuàng)建一個新的路徑
        ctx.beginPath();
    }, false);????
    //?監(jiān)聽鼠標(biāo)移動????
    canvas.addEventListener("mousemove",?({?x,?y?})?=>?{????????????
      //?沒有按下就不管
      if?(!isDown)?return;
      //?調(diào)用繪制方法
      draw(lastX,?lastY,?x,?y);
      //?把當(dāng)前移動時的坐標(biāo)作為下一次的繪制路徑的起點(diǎn)
      lastX?=?x;
      lastY?=?y;
    }, false);
    //?監(jiān)聽鼠標(biāo)抬起
    canvas.addEventListener("mouseup", ()?=>?{
      isDown?=?false;
      //?關(guān)閉路徑
      ctx.closePath();
    },?false);
    // 監(jiān)聽鼠標(biāo)移出
    canvas.addEventListener("mouseleave", () => {
        // 移出canvas范圍,也認(rèn)為是鼠標(biāo)抬起了,再移入需要重新按下鼠標(biāo),避免移出之后再抬起鼠標(biāo),重新進(jìn)入畫筆還能繼續(xù)畫的問題
        isDown = false;
        ctx.closePath();
    }, false);
};

以上代碼就實(shí)現(xiàn)了最基本的簽名功能。

將canvas導(dǎo)出為圖片

這個功能比較簡單,思路都在注釋里了

//?使用canvas的toDataURL()方法,將畫布內(nèi)容轉(zhuǎn)換為base64格式的圖片數(shù)據(jù):
let?imgData?=?canvas.toDataURL('image/png');?
//?創(chuàng)建下載鏈接
let?link?=?document.createElement('a');
link.download?=?'picture.png';
link.href?=?imgData;
//?觸發(fā)點(diǎn)擊
link.click();
//?移除元素
document.body.removeChild(link);

撤銷和重寫功能

整理思路

  • 要實(shí)現(xiàn)撤銷筆畫回到上一步,就要知道上一步畫了什么,就是要記錄下來,我們可以用一個數(shù)組,把每次鼠標(biāo)移動時得到的坐標(biāo)放進(jìn)去。

  • 通過基礎(chǔ)功能我們知道了,畫布上簽名,是由多個線條組成的,而線條是由很多個點(diǎn)組成的。那我們撤銷的時候,是撤銷一條線,即一個筆畫,而不是一個點(diǎn)。

  • 那么,怎么知道哪些點(diǎn)是屬于一個筆畫的呢,就是要給這些點(diǎn)分組,一個筆畫為一組。我們規(guī)定,從鼠標(biāo)按下到鼠標(biāo)抬起,這之間移動時產(chǎn)生的所有點(diǎn)為一組,即一個筆畫。用代碼表示,就是有多個數(shù)組,所以我們定一個二維數(shù)組來保存所有的點(diǎn)。

改寫一下前面的代碼

window.onload?=?function?()?{????
    //?默認(rèn)鼠標(biāo)是沒有按下的????
    let?isDown?=?false;????
    //?//?記錄上一次鼠標(biāo)的位置????
    //?let?lastX?=?0;?//?x軸????
    //?let?lastY?=?0;?//?y軸??????
    //?這次要用數(shù)組來記錄????
    let?points?=?[];?//?這是一個筆畫的點(diǎn)????
    let?allPonits?=?[];?//?這是所有筆畫的點(diǎn)????????
    //?獲取canvas元素?????
    const?canvas?=?document.getElementById("canvas");????//?獲取canvas的上下文????
    const?ctx?=?canvas.getContext("2d");????//?定義線條的寬度,即畫筆的粗細(xì)????
    ctx.lineWidth?=?3;????//?定義畫筆的顏色????
    ctx.strokeStyle?=?"#000";????????
    function?draw(startX, startY, endX, endY)?{????????
        //?起點(diǎn)????????
        ctx.moveTo(startX, startY);????????
        //?終點(diǎn)????????
        ctx.lineTo(endX, endY);????????
        // 調(diào)用 stroke,即可看到繪制的線條????????
        ctx.stroke();????
    }????
    //?監(jiān)聽鼠標(biāo)按下,得到按下時鼠標(biāo)在畫布上的坐標(biāo)????
    canvas.addEventListener("mousedown", ({?x,?y?})?=>?{????????????
        isDown?=?true;????????????
        //?lastX?=?x;
        //?lastY?=?y;
        // 保存當(dāng)前坐標(biāo)作為起點(diǎn)
        points.push({?x,?y?});????????????
        //?創(chuàng)建一個新的路徑????????????
        ctx.beginPath();????????
     }, false);????
     //?監(jiān)聽鼠標(biāo)移動????
     canvas.addEventListener("mousemove", ({?x,?y?})?=>?{
         //?沒有按下就不管
         if?(!isDown)?return;
         //?調(diào)用繪制方法
         //?draw(lastX,?lastY,?x,?y);
         //?把當(dāng)前移動時的坐標(biāo)作為下一次的繪制路徑的起點(diǎn)????????????
         //?lastX?=?x;
         //?lastY?=?y;
         // 每次都取最后一個點(diǎn),作為繪制的起點(diǎn)
         const?lastPoint?=?points.at(-1);
         draw(lastPoint.x,?lastPoint.y,?x,?y);
         //?把當(dāng)前的點(diǎn)保存起來,又作為下一次繪制的起點(diǎn)
         points.push({?x,?y?});
     }, false);
     //?監(jiān)聽鼠標(biāo)抬起????
     canvas.addEventListener("mouseup", (e)?=>?{????????????
         isDown?=?false;
         //?關(guān)閉路徑
         ctx.closePath();
         //?鼠標(biāo)抬起,說明當(dāng)前這一筆就結(jié)束了,把這一筆的所有點(diǎn)的數(shù)組放到總的里面
         allPonits.push(points);
         //?清空這一筆畫,為下一筆畫做準(zhǔn)備????????????
         points?=?[];
     }, false);
    // 監(jiān)聽鼠標(biāo)移出
    canvas.addEventListener("mouseleave", () => {
        // 移出canvas范圍,也認(rèn)為是鼠標(biāo)抬起了,再移入需要重新按下鼠標(biāo),避免移出之后再抬起鼠標(biāo),重新進(jìn)入畫筆還能繼續(xù)畫的問題
        isDown = false;
        // 關(guān)閉畫筆
        ctx.closePath();
        // 如果是先抬起鼠標(biāo)再移出,那么points里面為空,就不保存了
        // 如果是先移出范圍,移出時就保存,這樣也不會觸發(fā)上面的監(jiān)聽鼠標(biāo)抬起事件,也不會push。
        if (points.length) {
            allPonits.push(points);
        }
        // 移出時也清空,因?yàn)闊o法判斷是先抬起還是先移出的。
        points = [];
    }, false);
 };

在頁面上加兩個按鈕

<div>
??<button?id="prev">上一步</button>
??<button?id="reset">重寫</button>
</div>
const?prev?=?document.getElementById("prev");
const?reset?=?document.getElementById("reset"); 
//?清空畫布
function?resetPath()?{
    ctx.clearRect(0,?0,?canvas.width,?canvas.height);
}
//?上一步
prev.addEventListener("click",?(e)?=>?{
    //?canvas本身不會記錄用戶的每一步操作
    //?要回到上一步,只能一次性清空所有的
    resetPath();
    //?刪除最后一個筆畫
    allPonits.pop();
    //?遍歷所有的筆畫并重新繪制
    // allPoints 是個二維數(shù)組
    allPonits.forEach((ps)?=>?{
        ps.forEach((item,?index)?=>?{
          // 下一個坐標(biāo)點(diǎn)
          let?next?=?ps[index?+?1];
          if?(next)?{
            // 有下一個點(diǎn)才執(zhí)行,否則到最后一個會報錯
            // 開始重新繪制
            ctx.beginPath();
            draw(item.x,?item.y,?next.x,?next.y);
            ctx.closePath();
          }
       });??
    });
});
//?重寫
reset.addEventListener("click", ()?=>?{
    //?點(diǎn)擊重寫時清空畫布,并清空所有的點(diǎn)????
    resetPath();
    allPonits?=?[];
}, false);

到這里,我們就完成了canvas手寫簽名,并且實(shí)現(xiàn)了撤銷和重寫,以及導(dǎo)出為圖片的功能。

以上就是JavaScript使用canvas實(shí)現(xiàn)手寫簽名功能的詳細(xì)內(nèi)容,更多關(guān)于JavaScript canvas手寫簽名的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解小程序中h5頁面onShow實(shí)現(xiàn)及跨頁面通信方案

    詳解小程序中h5頁面onShow實(shí)現(xiàn)及跨頁面通信方案

    這篇文章主要介紹了小程序中h5頁面onShow實(shí)現(xiàn)及跨頁面通信方案,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-05-05
  • 項目中常用的JS方法整理

    項目中常用的JS方法整理

    這里給大家整理的是本人上個項目中所用到的js方法,都是些非常常用的javascript方法,相信小伙伴們也能經(jīng)常用到,這里整理出來分享給大家。
    2015-01-01
  • vue iView 上傳組件之手動上傳功能

    vue iView 上傳組件之手動上傳功能

    iView 主要服務(wù)于 PC 界面的中后臺業(yè)務(wù),是一套高質(zhì)量的開源 UI 組件庫。這篇文章主要介紹了iView 上傳組件之手動上傳功能,需要的朋友可以參考下
    2018-03-03
  • js實(shí)現(xiàn)人才網(wǎng)站職位選擇功能的方法

    js實(shí)現(xiàn)人才網(wǎng)站職位選擇功能的方法

    這篇文章主要介紹了js實(shí)現(xiàn)人才網(wǎng)站職位選擇功能的方法,涉及javascript動態(tài)操作頁面元素結(jié)點(diǎn)的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-08-08
  • js select下拉聯(lián)動 更具級聯(lián)性!

    js select下拉聯(lián)動 更具級聯(lián)性!

    這篇文章主要為大家詳細(xì)介紹了js select下拉聯(lián)動的相關(guān)資料,更具級聯(lián)性!文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • uniapp微信小程序多環(huán)境配置以及使用教程

    uniapp微信小程序多環(huán)境配置以及使用教程

    前后端分離開發(fā)模式中,無論前后端都有可能區(qū)分不同的環(huán)境配置,下面這篇文章主要給大家介紹了關(guān)于uniapp微信小程序多環(huán)境配置以及使用的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • 靜態(tài)的動態(tài)續(xù)篇之來點(diǎn)XML

    靜態(tài)的動態(tài)續(xù)篇之來點(diǎn)XML

    靜態(tài)的動態(tài)續(xù)篇之來點(diǎn)XML...
    2006-08-08
  • 中文字符串截取的js函數(shù)代碼

    中文字符串截取的js函數(shù)代碼

    有時在顯示某段文字的時候,可能會太長,影響我們頁面的顯示效果,如果僅是英文,那么我們可以用String.substring(start, end)函數(shù)就已經(jīng)夠用了,但是通常我們都會遇到既有英文,又有漢字的情況
    2013-04-04
  • undefined與null的區(qū)別示例詳解

    undefined與null的區(qū)別示例詳解

    這篇文章主要為大家介紹了undefined與null的區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • IE6下focus與blur錯亂的解決方案

    IE6下focus與blur錯亂的解決方案

    Miller同學(xué)發(fā)現(xiàn)的IE6 bug:如以下代碼,點(diǎn)擊textarea時,引發(fā)window的blur,導(dǎo)致focus與blur配對混亂
    2011-07-07

最新評論

张家港市| 汤阴县| 腾冲县| 潜山县| 鄂托克旗| 安平县| 武威市| 府谷县| 丹东市| 莲花县| 唐山市| 衡阳县| 鄂托克前旗| 南京市| 内江市| 安阳市| 潼关县| 桑植县| 无极县| 普陀区| 新余市| 资溪县| 潍坊市| 泰和县| 凤阳县| 诸暨市| 长乐市| 富裕县| 外汇| 东丰县| 阳泉市| 丹阳市| 芜湖县| 富阳市| 沙湾县| 林芝县| 祁门县| 平利县| 浦江县| 阿坝县| 开化县|