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

前端JavaScript實現(xiàn)簡單的在線電子簽名工具

 更新時間:2025年09月26日 15:17:08   作者:前端Hardy  
這篇文章主要為大家詳細介紹了前端JavaScript實現(xiàn)簡單的在線電子簽名工具的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

該 HTML 文件是一個功能完整、跨設備兼容的在線電子簽名工具,基于 HTML5 Canvas 實現(xiàn)簽名繪制核心功能,搭配直觀的樣式控制(顏色、線條粗細)與操作按鈕(清除、保存、下載),同時支持鼠標與觸摸屏交互,整體設計簡潔易用且視覺友好。

大家復制代碼時,可能會因格式轉(zhuǎn)換出現(xiàn)錯亂,導致樣式失效。建議先少量復制代碼進行測試,若未能解決問題,私信回復源碼兩字,我會發(fā)送完整的壓縮包給你。

演示效果

HTML&CSS

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>在線電子簽名工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
        }

        .container {
            max-width: 800px;
            width: 100%;
            background: white;
            border-radius: 15px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            padding: 30px;
            margin-top: 20px;
        }

        header {
            text-align: center;
            margin-bottom: 30px;
        }

        h1 {
            color: #2c3e50;
            margin-bottom: 10px;
            font-size: 2.2rem;
        }

        .subtitle {
            color: #7f8c8d;
            font-size: 1.1rem;
        }

        .signature-area {
            border: 2px dashed #bdc3c7;
            border-radius: 10px;
            margin-bottom: 25px;
            position: relative;
            background-color: #f9f9f9;
            overflow: hidden;
        }

        #signatureCanvas {
            width: 100%;
            height: 300px;
            display: block;
            cursor: crosshair;
            background-color: white;
        }

        .canvas-placeholder {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            color: #95a5a6;
            font-size: 1.2rem;
            pointer-events: none;
        }

        .controls {
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
            margin-bottom: 25px;
        }

        .btn {
            padding: 12px 20px;
            border: none;
            border-radius: 6px;
            font-size: 1rem;
            cursor: pointer;
            transition: all 0.3s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }

        .btn-clear {
            background-color: #e74c3c;
            color: white;
        }

        .btn-save {
            background-color: #2ecc71;
            color: white;
        }

        .btn-download {
            background-color: #3498db;
            color: white;
        }

        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
        }

        .btn:active {
            transform: translateY(0);
        }

        .btn-clear:hover {
            background-color: #c0392b;
        }

        .btn-save:hover {
            background-color: #27ae60;
        }

        .btn-download:hover {
            background-color: #2980b9;
        }

        .color-picker {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 20px;
        }

        .color-option {
            width: 30px;
            height: 30px;
            border-radius: 50%;
            cursor: pointer;
            border: 2px solid transparent;
            transition: transform 0.2s;
        }

        .color-option:hover {
            transform: scale(1.1);
        }

        .color-option.active {
            border-color: #2c3e50;
            transform: scale(1.1);
        }

        .line-width {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 20px;
        }

        .line-width input {
            width: 100%;
        }

        .signature-preview {
            margin-top: 30px;
            text-align: center;
        }

        .preview-title {
            margin-bottom: 15px;
            color: #2c3e50;
        }

        #signaturePreview {
            max-width: 100%;
            border: 1px solid #bdc3c7;
            border-radius: 5px;
            display: none;
        }

        footer {
            margin-top: 30px;
            text-align: center;
            color: #7f8c8d;
            font-size: 0.9rem;
        }

        @media (max-width: 600px) {
            .container {
                padding: 20px;
            }

            h1 {
                font-size: 1.8rem;
            }

            .controls {
                flex-direction: column;
            }

            .btn {
                width: 100%;
            }
        }

        .instructions {
            background-color: #f8f9fa;
            border-left: 4px solid #3498db;
            padding: 15px;
            margin-bottom: 25px;
            border-radius: 0 8px 8px 0;
        }

        .instructions h3 {
            color: #2c3e50;
            margin-bottom: 10px;
        }

        .instructions ul {
            padding-left: 20px;
        }

        .instructions li {
            margin-bottom: 8px;
        }
    </style>
</head>

<body>
    <header>
        <h1>在線電子簽名工具</h1>
        <p class="subtitle">使用鼠標或觸摸屏創(chuàng)建您的電子簽名</p>
    </header>

    <div class="container">
        <div class="instructions">
            <h3>使用說明</h3>
            <ul>
                <li>在下方畫布區(qū)域使用鼠標或手指(觸摸屏設備)繪制您的簽名</li>
                <li>可以使用下方的顏色和線條粗細選項調(diào)整簽名樣式</li>
                <li>完成后可以保存簽名或下載為PNG圖片</li>
                <li>如需重新開始,請點擊"清除簽名"按鈕</li>
            </ul>
        </div>

        <div class="signature-area">
            <canvas id="signatureCanvas"></canvas>
            <div class="canvas-placeholder">請在此處繪制您的簽名</div>
        </div>

        <div class="color-picker">
            <span>選擇顏色:</span>
            <div class="color-option active" style="background-color: #000000;" data-color="#000000"></div>
            <div class="color-option" style="background-color: #e74c3c;" data-color="#e74c3c"></div>
            <div class="color-option" style="background-color: #3498db;" data-color="#3498db"></div>
            <div class="color-option" style="background-color: #2ecc71;" data-color="#2ecc71"></div>
            <div class="color-option" style="background-color: #f39c12;" data-color="#f39c12"></div>
        </div>

        <div class="line-width">
            <span>線條粗細:</span>
            <input type="range" id="lineWidth" min="1" max="10" value="2">
            <span id="widthValue">2px</span>
        </div>

        <div class="controls">
            <button class="btn btn-clear" id="clearBtn">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
                    <path
                        d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z" />
                    <path fill-rule="evenodd"
                        d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z" />
                </svg>
                清除簽名
            </button>
            <button class="btn btn-save" id="saveBtn">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
                    <path
                        d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H9.5a1 1 0 0 0-1 1v7.293l2.646-2.647a.5.5 0 0 1 .708.708l-3.5 3.5a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L7.5 9.293V2a2 2 0 0 1 2-2H14a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h2.5a.5.5 0 0 1 0 1H2z" />
                </svg>
                保存簽名
            </button>
            <button class="btn btn-download" id="downloadBtn">
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
                    <path
                        d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" />
                    <path
                        d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z" />
                </svg>
                下載簽名
            </button>
        </div>

        <div class="signature-preview">
            <h3 class="preview-title">簽名預覽</h3>
            <img id="signaturePreview" alt="簽名預覽">
        </div>
    </div>

    <footer>
        <p>? 2023 在線電子簽名工具 | 使用HTML5 Canvas和JavaScript構建</p>
    </footer>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            // 獲取Canvas元素和上下文
            const canvas = document.getElementById('signatureCanvas');
            const ctx = canvas.getContext('2d');
            const placeholder = document.querySelector('.canvas-placeholder');

            // 獲取控制元素
            const clearBtn = document.getElementById('clearBtn');
            const saveBtn = document.getElementById('saveBtn');
            const downloadBtn = document.getElementById('downloadBtn');
            const colorOptions = document.querySelectorAll('.color-option');
            const lineWidthInput = document.getElementById('lineWidth');
            const widthValue = document.getElementById('widthValue');
            const signaturePreview = document.getElementById('signaturePreview');

            // 設置Canvas尺寸
            function resizeCanvas() {
                const container = canvas.parentElement;
                canvas.width = container.clientWidth;
                canvas.height = 300;

                // 重新繪制內(nèi)容(如果有)
                redrawSignature();
            }

            // 初始化變量
            let isDrawing = false;
            let lastX = 0;
            let lastY = 0;
            let currentColor = '#000000';
            let currentLineWidth = 2;
            let signatureData = [];

            // 初始化Canvas
            resizeCanvas();
            window.addEventListener('resize', resizeCanvas);

            // 設置初始繪制樣式
            ctx.strokeStyle = currentColor;
            ctx.lineWidth = currentLineWidth;
            ctx.lineJoin = 'round';
            ctx.lineCap = 'round';

            // 顏色選擇
            colorOptions.forEach(option => {
                option.addEventListener('click', function () {
                    // 移除所有active類
                    colorOptions.forEach(opt => opt.classList.remove('active'));
                    // 添加active類到當前選項
                    this.classList.add('active');
                    // 更新當前顏色
                    currentColor = this.getAttribute('data-color');
                    ctx.strokeStyle = currentColor;
                });
            });

            // 線條粗細
            lineWidthInput.addEventListener('input', function () {
                currentLineWidth = this.value;
                ctx.lineWidth = currentLineWidth;
                widthValue.textContent = `${currentLineWidth}px`;
            });

            // 開始繪制
            function startDrawing(e) {
                isDrawing = true;
                [lastX, lastY] = getCoordinates(e);
                placeholder.style.display = 'none';

                // 開始新的路徑
                signatureData.push([]);
            }

            // 繪制中
            function draw(e) {
                if (!isDrawing) return;

                e.preventDefault();

                const [x, y] = getCoordinates(e);

                // 繪制到Canvas
                ctx.beginPath();
                ctx.moveTo(lastX, lastY);
                ctx.lineTo(x, y);
                ctx.stroke();

                // 保存繪制數(shù)據(jù)
                signatureData[signatureData.length - 1].push({ x, y });

                [lastX, lastY] = [x, y];
            }

            // 結束繪制
            function stopDrawing() {
                isDrawing = false;
            }

            // 獲取坐標(兼容鼠標和觸摸事件)
            function getCoordinates(e) {
                let x, y;

                if (e.type.includes('touch')) {
                    x = e.touches[0].clientX - canvas.getBoundingClientRect().left;
                    y = e.touches[0].clientY - canvas.getBoundingClientRect().top;
                } else {
                    x = e.offsetX;
                    y = e.offsetY;
                }

                return [x, y];
            }

            // 重新繪制簽名(用于Canvas尺寸調(diào)整后)
            function redrawSignature() {
                ctx.clearRect(0, 0, canvas.width, canvas.height);

                if (signatureData.length === 0) {
                    placeholder.style.display = 'flex';
                    return;
                }

                ctx.beginPath();

                signatureData.forEach(stroke => {
                    if (stroke.length > 0) {
                        ctx.moveTo(stroke[0].x, stroke[0].y);

                        for (let i = 1; i < stroke.length; i++) {
                            ctx.lineTo(stroke[i].x, stroke[i].y);
                        }
                    }
                });

                ctx.stroke();
            }

            // 清除簽名
            function clearSignature() {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                signatureData = [];
                placeholder.style.display = 'flex';
                signaturePreview.style.display = 'none';
            }

            // 保存簽名預覽
            function saveSignature() {
                if (signatureData.length === 0) {
                    alert('請先繪制簽名!');
                    return;
                }

                const dataURL = canvas.toDataURL('image/png');
                signaturePreview.src = dataURL;
                signaturePreview.style.display = 'block';

                // 平滑滾動到預覽區(qū)域
                signaturePreview.scrollIntoView({ behavior: 'smooth' });
            }

            // 下載簽名
            function downloadSignature() {
                if (signatureData.length === 0) {
                    alert('請先繪制簽名!');
                    return;
                }

                const dataURL = canvas.toDataURL('image/png');
                const link = document.createElement('a');
                link.download = '電子簽名.png';
                link.href = dataURL;
                link.click();
            }

            // 事件監(jiān)聽器
            canvas.addEventListener('mousedown', startDrawing);
            canvas.addEventListener('mousemove', draw);
            canvas.addEventListener('mouseup', stopDrawing);
            canvas.addEventListener('mouseout', stopDrawing);

            // 觸摸事件支持
            canvas.addEventListener('touchstart', startDrawing);
            canvas.addEventListener('touchmove', draw);
            canvas.addEventListener('touchend', stopDrawing);

            clearBtn.addEventListener('click', clearSignature);
            saveBtn.addEventListener('click', saveSignature);
            downloadBtn.addEventListener('click', downloadSignature);

            // 防止觸摸設備上的滾動
            canvas.addEventListener('touchmove', function (e) {
                if (isDrawing) {
                    e.preventDefault();
                }
            }, { passive: false });
        });
    </script>
</body>

</html>

HTML

  • container: 核心功能容器:包裹使用說明、簽名畫布、樣式控制、操作按鈕與預覽區(qū),集中承載工具功能
  • instructions: 使用說明區(qū):通過列表清晰告知用戶操作步驟(繪制、調(diào)整樣式、保存等),降低使用門檻
  • signature-area: 簽名繪制區(qū):包含 Canvas 畫布與占位提示,是工具的核心交互區(qū)域
  • signatureCanvas:簽名繪制核心:基于 HTML5 Canvas 實現(xiàn)手寫簽名功能,支持像素級繪制
  • canvas-placeholder:畫布占位提示:未繪制時顯示 “請在此處繪制您的簽名”,引導用戶操作,繪制后隱藏
  • color-picker:顏色選擇區(qū):提供 5 種預設顏色(黑、紅、藍、綠、橙),支持點擊切換簽名顏色
  • color-option:單個顏色選項:圓形色塊,active 類標識當前選中顏色,data-color 存儲顏色值
  • line-width:線條粗細控制區(qū):包含滑塊輸入框與數(shù)值顯示,支持 1-10px 的粗細調(diào)節(jié)
  • lineWidth:粗細調(diào)節(jié)滑塊:min=1/max=10/value=2,實時同步線條粗細
  • controls:操作按鈕容器:橫向排列 “清除”“保存”“下載” 三個核心功能按鈕,方便用戶操作
  • btn:功能按鈕:每個按鈕綁定不同操作,內(nèi)嵌 SVG 圖標增強視覺辨識度(如 “清除” 對應垃圾桶圖標)
  • svg:按鈕圖標:使用內(nèi)聯(lián) SVG 繪制功能圖標(清除、保存、下載),無需外部圖片資源,加載更快
  • signature-preview:簽名預覽區(qū):顯示保存后的簽名圖片,提供可視化反饋,未保存時隱藏
  • signaturePreview":預覽圖片容器:src 動態(tài)賦值為 Canvas 導出的圖片數(shù)據(jù),display 控制顯示隱藏

CSS

  • container 功能容器:1. 尺寸:最大寬度 800px、100% 自適應寬度,確保大屏幕不拉伸、小屏幕全屏;2. 視覺:白色背景(white)、圓角 15px、陰影(0 10px 30px rgba(0,0,0,0.1)),模擬卡片效果,與漸變背景區(qū)分,突出功能區(qū);3. 內(nèi)邊距:padding: 30px,確保內(nèi)部元素不擁擠。
  • .header 頭部信息區(qū):1. 布局:text-align: center 使標題 / 副標題居中,margin-bottom: 30px 與下方功能區(qū)分隔;2. 文字:標題(h1)顏色深灰藍(#2c3e50)、字號 2.2rem,副標題(.subtitle)顏色淺灰(#7f8c8d)、字號 1.1rem,層級分明。
  • .instructions 使用說明區(qū):1. 視覺:淺灰背景(#f8f9fa)、左側 4px 藍色邊框(#3498db)、圓角(0 8px 8px 0),與其他區(qū)域區(qū)分,引導用戶注意;2. 內(nèi)邊距:padding: 15px,列表左 padding 20px,確保文字不貼邊。
  • .signature-area 簽名繪制區(qū):1. 邊框:2px 虛線(dashed #bdc3c7)、圓角 10px,明確繪制范圍;2. 背景:淺灰(#f9f9f9),與 Canvas 白色背景區(qū)分;3. 定位:position: relative,為絕對定位的占位提示提供父容器;4. 溢出:overflow: hidden,防止 Canvas 內(nèi)容超出容器。
  • #signatureCanvas 簽名畫布:1. 尺寸:寬度 100%、高度 300px,填滿父容器;2. 交互:cursor: crosshair,鼠標懸停時顯示 “十字準星”,提示可繪制;3. 背景:白色(white),模擬紙質(zhì)簽名效果。
  • .canvas-placeholder 畫布占位提示:1. 定位:absolute 覆蓋整個 Canvas,top/left: 0、width/height: 100%;2. 布局:flex 居中顯示文字,pointer-events: none 確保不遮擋 Canvas 交互;3. 文字:淺灰(#95a5a6)、字號 1.2rem,提示用戶操作。
  • .color-picker/.line-width 樣式控制區(qū):1. 布局:display: flex+align-items: center 使標簽與選項垂直居中,gap: 10px 分隔元素,margin-bottom: 20px 與其他區(qū)域分隔;2. 顏色選項(.color-option):圓形(border-radius: 50%)、30×30px 尺寸,active 類添加深灰邊框(#2c3e50),明確選中狀態(tài)。
  • .controls 操作按鈕容器:1. 布局:display: flex+flex-wrap: wrap(支持換行)、gap: 15px,適配不同屏幕寬度;2. 響應式:小屏幕自動換行,確保按鈕不溢出。
  • .btn 功能按鈕:1. 基礎樣式:padding: 12px 20px、無邊框、圓角 6px、字號 1rem,display: flex+align-items: center+gap: 8px 使圖標與文字居中;2. 顏色區(qū)分:清除(紅#e74c3c)、保存(綠#2ecc71)、下載(藍#3498db),通過顏色暗示功能(紅 = 刪除、綠 = 保存、藍 = 下載);3. 交互:transition: all 0.3s ease,hover 時上移 2px(translateY(-2px))+ 陰影,active 時復位,提供點擊反饋。
  • .signature-preview 簽名預覽區(qū):1. 布局:text-align: center 使預覽圖居中,margin-top: 30px 與操作區(qū)分隔;2. 預覽圖(#signaturePreview):max-width: 100%避免拉伸,邊框(1px solid #bdc3c7)+ 圓角 5px,默認 display: none,保存后顯示。
  • footer 頁腳信息區(qū):1. 布局:margin-top: 30px、text-align: center,顏色淺灰(#7f8c8d)、字號 0.9rem,不搶占視覺焦點;2. 內(nèi)容:版權 + 技術棧說明,增強工具可信度。

JavaScript

1.初始化與元素獲取

document.addEventListener('DOMContentLoaded', function () {
    // 1. 獲取Canvas與上下文(核心繪制對象)
    const canvas = document.getElementById('signatureCanvas');
    const ctx = canvas.getContext('2d'); // 2D繪制上下文,用于繪制簽名
    const placeholder = document.querySelector('.canvas-placeholder');

    // 2. 獲取控制元素(樣式控制、操作按鈕、預覽區(qū))
    const clearBtn = document.getElementById('clearBtn');
    const saveBtn = document.getElementById('saveBtn');
    const downloadBtn = document.getElementById('downloadBtn');
    const colorOptions = document.querySelectorAll('.color-option');
    const lineWidthInput = document.getElementById('lineWidth');
    const widthValue = document.getElementById('widthValue');
    const signaturePreview = document.getElementById('signaturePreview');

    // 3. 初始化狀態(tài)變量
    let isDrawing = false; // 是否正在繪制(防止鼠標離開后繼續(xù)繪制)
    let lastX = 0, lastY = 0; // 上一個繪制點的坐標(用于連接線條)
    let currentColor = '#000000'; // 默認簽名顏色(黑色)
    let currentLineWidth = 2; // 默認線條粗細(2px)
    let signatureData = []; // 存儲簽名數(shù)據(jù)(用于Canvas resize后重新繪制)
});

2.Canvas 尺寸適配(核心兼容邏輯)

解決窗口 resize 時 Canvas 內(nèi)容丟失問題,通過保存繪制數(shù)據(jù)重新渲染:

function resizeCanvas() {
    const container = canvas.parentElement;
    // 1. 更新Canvas尺寸為父容器寬度(高度固定300px)
    canvas.width = container.clientWidth;
    canvas.height = 300;
    // 2. 重新繪制簽名(如果有數(shù)據(jù))
    redrawSignature();
}

// 重新繪制簽名:遍歷保存的繪制數(shù)據(jù),還原簽名
function redrawSignature() {
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空畫布
    if (signatureData.length === 0) { // 無簽名數(shù)據(jù)時顯示占位提示
        placeholder.style.display = 'flex';
        return;
    }

    // 遍歷每一段繪制路徑(每段對應一次鼠標按下到抬起的繪制)
    signatureData.forEach(stroke => {
        if (stroke.length > 0) {
            ctx.beginPath(); // 開始新路徑
            ctx.moveTo(stroke[0].x, stroke[0].y); // 移動到第一段的起點
            // 連接后續(xù)所有點,還原線條
            for (let i = 1; i < stroke.length; i++) {
                ctx.lineTo(stroke[i].x, stroke[i].y);
            }
        }
    });
    ctx.stroke(); // 執(zhí)行繪制
}

// 初始化時調(diào)用一次,窗口 resize 時重新適配
resizeCanvas();
window.addEventListener('resize', resizeCanvas);

3.簽名繪制邏輯(核心交互)

通過監(jiān)聽鼠標 / 觸摸事件,實現(xiàn) “按下 - 移動 - 抬起” 的完整繪制流程,兼容 PC 與移動端:

// 1. 初始化繪制樣式:圓角線條(避免尖銳邊緣)
ctx.strokeStyle = currentColor; // 線條顏色
ctx.lineWidth = currentLineWidth; // 線條粗細
ctx.lineJoin = 'round'; // 線條連接點圓角
ctx.lineCap = 'round'; // 線條端點圓角

// 2. 開始繪制(鼠標按下/觸摸開始)
function startDrawing(e) {
    isDrawing = true; // 標記為正在繪制
    [lastX, lastY] = getCoordinates(e); // 獲取初始坐標(兼容鼠標/觸摸)
    placeholder.style.display = 'none'; // 隱藏占位提示
    signatureData.push([]); // 新增一段繪制數(shù)據(jù)(存儲當前筆畫)
}

// 3. 繪制中(鼠標移動/觸摸滑動)
function draw(e) {
    if (!isDrawing) return; // 未標記繪制時跳過
    e.preventDefault(); // 阻止觸摸設備上的默認滾動

    const [x, y] = getCoordinates(e); // 獲取當前坐標
    // 繪制線條:從上次坐標到當前坐標
    ctx.beginPath();
    ctx.moveTo(lastX, lastY);
    ctx.lineTo(x, y);
    ctx.stroke();

    // 保存當前坐標到繪制數(shù)據(jù)(用于后續(xù)重新渲染)
    signatureData[signatureData.length - 1].push({ x, y });
    [lastX, lastY] = [x, y]; // 更新上次坐標
}

// 4. 結束繪制(鼠標抬起/觸摸結束)
function stopDrawing() {
    isDrawing = false; // 取消繪制標記
}

// 5. 坐標轉(zhuǎn)換(兼容鼠標與觸摸事件)
function getCoordinates(e) {
    let x, y;
    if (e.type.includes('touch')) { // 觸摸事件:獲取觸摸點相對于Canvas的坐標
        x = e.touches[0].clientX - canvas.getBoundingClientRect().left;
        y = e.touches[0].clientY - canvas.getBoundingClientRect().top;
    } else { // 鼠標事件:直接獲取相對于Canvas的偏移坐標
        x = e.offsetX;
        y = e.offsetY;
    }
    return [x, y];
}

// 6. 綁定繪制事件(鼠標+觸摸)
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing); // 鼠標離開畫布時結束繪制

canvas.addEventListener('touchstart', startDrawing);
canvas.addEventListener('touchmove', draw);
canvas.addEventListener('touchend', stopDrawing);

// 防止觸摸繪制時頁面滾動(passive: false 允許 preventDefault)
canvas.addEventListener('touchmove', function (e) {
    if (isDrawing) e.preventDefault();
}, { passive: false });

4.樣式控制邏輯(顏色 + 粗細)

實現(xiàn)簽名樣式的實時調(diào)整,視覺反饋即時:

// 顏色選擇:點擊顏色塊切換簽名顏色
colorOptions.forEach(option => {
    option.addEventListener('click', function () {
        // 移除所有選項的active類,避免多選中
        colorOptions.forEach(opt => opt.classList.remove('active'));
        // 為當前選中選項添加active類,視覺標識
        this.classList.add('active');
        // 更新當前顏色,并同步到Canvas繪制樣式
        currentColor = this.getAttribute('data-color');
        ctx.strokeStyle = currentColor;
    });
});

// 線條粗細:拖動滑塊調(diào)整,實時顯示數(shù)值
lineWidthInput.addEventListener('input', function () {
    currentLineWidth = this.value;
    ctx.lineWidth = currentLineWidth; // 同步到Canvas繪制樣式
    widthValue.textContent = `${currentLineWidth}px`; // 更新數(shù)值顯示
});

以上就是前端JavaScript實現(xiàn)簡單的在線電子簽名工具的詳細內(nèi)容,更多關于前端在線電子簽名工具的資料請關注腳本之家其它相關文章!

相關文章

最新評論

天气| 项城市| 安泽县| 澜沧| 秦安县| 安徽省| 和田市| 大安市| 新竹市| 雷州市| 托里县| 太湖县| 武鸣县| 东光县| 高邮市| 遂平县| 揭西县| 延边| 梁河县| 偏关县| 蓬莱市| 旺苍县| 台北市| 新宁县| 洪湖市| 九寨沟县| 铁岭县| 库伦旗| 新和县| 明星| 定西市| 犍为县| 宜都市| 时尚| 南和县| 都匀市| 启东市| 阿鲁科尔沁旗| 仙游县| 玉田县| 项城市|