JS+HTML實(shí)現(xiàn)在線圖片水印添加工具
概述
在社交媒體和內(nèi)容創(chuàng)作日益頻繁的今天,如何保護(hù)原創(chuàng)內(nèi)容、展示品牌身份成了一個(gè)不得不面對(duì)的問題。給圖片添加水印,正是目前主流平臺(tái)和創(chuàng)作者最常用的解決方案之一。
但問題也隨之而來:
- Photoshop 加水印太麻煩?
- 腳本工具不好操作?
- 找不到一個(gè)輕便、美觀、好用的工具?
別急,今天帶大家實(shí)現(xiàn)一個(gè)完全基于 HTML + CSS 構(gòu)建的現(xiàn)代化 「圖片水印在線工具」,無需安裝、純前端交互,支持在線預(yù)覽和個(gè)性化定制,一切只需瀏覽器即可完成!
功能亮點(diǎn)
該工具界面簡潔、響應(yīng)迅速,適合各類用戶在線添加水印。以下是主要功能一覽:
1. 拖拽上傳圖片
- 支持點(diǎn)擊上傳與拖拽上傳;
- 自帶上傳動(dòng)畫與視覺反饋;
- 支持預(yù)覽已上傳的圖片內(nèi)容。
2. 水印自定義設(shè)置
- 支持文本水印和圖像水?。蓴U(kuò)展);
- 自定義顏色、字體、大小、透明度、位置;
- 所有設(shè)置實(shí)時(shí)預(yù)覽,無需刷新。
3. 響應(yīng)式設(shè)計(jì)
- 使用 CSS Grid 進(jìn)行布局;
- 自動(dòng)適配移動(dòng)端和桌面端;
- sticky 面板在大屏上保持固定,操作方便。
4. 高顏值 UI
- 全局主題色可配置;
- 光影、圓角、漸變背景一應(yīng)俱全;
- UI 參考現(xiàn)代 SaaS 工具風(fēng)格。
使用方法
第一步:上傳你的圖片
點(diǎn)擊或拖拽圖片到上傳區(qū)域,支持 JPG、PNG 等常用格式。
<input type="file" accept="image/*" id="imageInput">
第二步:設(shè)置水印參數(shù)
通過設(shè)置面板可以自定義以下選項(xiàng):
設(shè)置項(xiàng) | 說明 |
---|---|
文本內(nèi)容 | 輸入水印文字 |
字體大小 | 使用 <input type="range"> 調(diào)整 |
字體顏色 | 使用 <input type="color"> 設(shè)置 |
透明度 | 調(diào)整水印透明度,范圍 0 ~ 1 |
位置選擇 | 左上、右上、居中、底部等 |
所有設(shè)置項(xiàng)變動(dòng)后會(huì)實(shí)時(shí)更新預(yù)覽區(qū)域的 Canvas。
第三步:查看預(yù)覽效果
頁面左側(cè) canvas 區(qū)域顯示當(dāng)前的最終效果。支持高清縮放。
<canvas id="previewCanvas" width="1080" height="2400"></canvas>
你可以隨時(shí)修改設(shè)置項(xiàng)查看實(shí)時(shí)預(yù)覽,真正做到“所見即所得”。
第四步:導(dǎo)出水印圖片(可擴(kuò)展)
可以通過擴(kuò)展 JS 腳本,調(diào)用 canvas.toBlob() 或 toDataURL() 將帶水印的圖像導(dǎo)出為下載鏈接,甚至上傳到云端。
技術(shù)解析
HTML:結(jié)構(gòu)清晰,語義為王
采用語義化標(biāo)簽 section、h1、label 等構(gòu)建主骨架,使結(jié)構(gòu)更清晰易讀,利于 SEO 和可維護(hù)性。
CSS:原子化變量設(shè)計(jì) + 現(xiàn)代布局方案
使用 :root 全局變量控制顏色、陰影、圓角等設(shè)計(jì)語言,一鍵更換主題毫無壓力。
關(guān)鍵點(diǎn):
CSS Grid + Flexbox 實(shí)現(xiàn)自適應(yīng)布局;
sticky 屬性打造固定側(cè)邊欄;
圖層陰影、過渡動(dòng)畫等增強(qiáng)交互體驗(yàn)。
--primary-color: #4361ee; --shadow: 0 4px 12px rgba(0,0,0,0.08); .settings { position: sticky; top: 20px; }
JavaScript(擴(kuò)展點(diǎn))
雖然本文代碼未包含完整 JS,但預(yù)留了事件鉤子,非常適合后續(xù)開發(fā):
- 圖片上傳:監(jiān)聽 input 或拖拽事件;
- 參數(shù)設(shè)置:綁定 input 和 change 事件;
- Canvas 繪制:結(jié)合 drawImage() 和文本渲染 API;
- 導(dǎo)出下載:使用 canvas.toBlob() 實(shí)現(xiàn)圖片導(dǎo)出。
延伸思考
除了當(dāng)前展示的功能,我們還可以這樣升級(jí)它:
1.添加圖像型水印
- 支持上傳一張 PNG 作為水印圖,疊加在主圖上;
- 調(diào)整透明度、縮放比例和位置。
2.批量處理功能
- 拖入多張圖片,逐一添加相同水??;
- 配合 Web Worker 實(shí)現(xiàn)多線程處理。
3.用戶配置本地保存
- 利用 localStorage 保存用戶上一次的設(shè)置;
- 實(shí)現(xiàn)個(gè)性化“水印模板”方案。
4.導(dǎo)出為高分辨率圖像
- 允許用戶自定義導(dǎo)出分辨率;
- 適配印刷或商用需求。
5.PWA 打包為 App
- 利用 PWA 特性將其打包為桌面應(yīng)用或手機(jī) App;
- 實(shí)現(xiàn)“離線水印工具”。
運(yùn)行效果
項(xiàng)目源碼下載
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>圖片水印工具</title> <style> :root { --primary-color: #4361ee; --primary-light: #e6e9ff; --secondary-color: #3f37c9; --text-color: #333; --light-text: #666; --border-color: #ddd; --bg-color: #f8f9fa; --card-bg: #fff; --shadow: 0 4px 12px rgba(0,0,0,0.08); } * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif; line-height: 1.6; color: var(--text-color); background-color: var(--bg-color); padding: 20px; } h1 { color: var(--primary-color); text-align: center; margin-bottom: 24px; font-weight: 600; } .container { display: grid; grid-template-columns: 1fr 350px; gap: 24px; max-width: 1400px; margin: 0 auto; } .preview { background: var(--card-bg); padding: 20px; border-radius: 12px; box-shadow: var(--shadow); display: flex; flex-direction: column; height: fit-content; } .preview-title { font-size: 18px; font-weight: 500; margin-bottom: 16px; color: var(--primary-color); display: flex; align-items: center; gap: 8px; } .preview-title svg { width: 20px; height: 20px; } #previewCanvas { width: 100%; max-width: 100%; height: auto; border-radius: 8px; border: 1px solid var(--border-color); background: repeating-conic-gradient(#f5f5f5 0% 25%, white 0% 50%) 50%/20px 20px; } .settings { background: var(--card-bg); padding: 24px; border-radius: 12px; box-shadow: var(--shadow); position: sticky; top: 20px; } .settings-title { font-size: 18px; font-weight: 500; margin-bottom: 20px; color: var(--primary-color); display: flex; align-items: center; gap: 8px; } .settings-title svg { width: 20px; height: 20px; } .form-group { margin-bottom: 20px; } label { display: block; margin-bottom: 8px; font-weight: 500; color: var(--light-text); font-size: 14px; } input[type="text"], input[type="number"], select { width: 100%; padding: 10px 12px; border: 1px solid var(--border-color); border-radius: 6px; font-size: 14px; transition: border-color 0.3s; } input[type="text"]:focus, input[type="number"]:focus, select:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 2px var(--primary-light); } input[type="file"] { display: none; } input[type="color"] { width: 40px; height: 40px; border: 1px solid var(--border-color); border-radius: 6px; padding: 2px; background: white; } .grid-container { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; } .range-container { display: flex; align-items: center; gap: 10px; } input[type="range"] { flex-grow: 1; height: 6px; border-radius: 3px; background: var(--border-color); -webkit-appearance: none; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--primary-color); cursor: pointer; } .range-value { min-width: 50px; text-align: right; font-size: 14px; color: var(--primary-color); } .watermark-type { display: none; } .watermark-type.active { display: block; } .section { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--border-color); } .section-title { font-size: 16px; font-weight: 500; margin-bottom: 16px; color: var(--primary-color); } button { width: 100%; padding: 12px; background-color: var(--primary-color); color: white; border: none; border-radius: 6px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background-color 0.3s; margin-top: 20px; display: flex; align-items: center; justify-content: center; gap: 8px; } button:hover { background-color: var(--secondary-color); } button svg { width: 18px; height: 18px; } @media (max-width: 768px) { .container { grid-template-columns: 1fr; } .settings { position: static; } } /* 添加一些圖標(biāo)樣式 */ .icon { width: 20px; height: 20px; fill: currentColor; } /* 文件上傳區(qū)域樣式 */ .file-upload-area { border: 2px dashed var(--border-color); border-radius: 8px; padding: 20px; text-align: center; cursor: pointer; transition: all 0.3s; margin-bottom: 15px; background-color: var(--bg-color); } .file-upload-area:hover { border-color: var(--primary-color); background-color: var(--primary-light); } .file-upload-area.drag-over { border-color: var(--primary-color); background-color: var(--primary-light); } .file-upload-icon { font-size: 48px; color: var(--primary-color); margin-bottom: 10px; } .file-upload-text { font-size: 14px; color: var(--light-text); } .file-upload-button { display: inline-block; padding: 8px 16px; background-color: var(--primary-color); color: white; border-radius: 4px; font-size: 14px; margin-top: 10px; transition: background-color 0.3s; } .file-upload-button:hover { background-color: var(--secondary-color); } .file-name { font-size: 14px; margin-top: 10px; color: var(--primary-color); word-break: break-all; } </style> </head> <body> <h1>圖片水印工具</h1> <div class="container"> <!-- 預(yù)覽區(qū)域 --> <div class="preview"> <h2 class="preview-title"> <svg class="icon" viewBox="0 0 24 24"> <path d="M19,19H5V5H19M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M13.96,12.29L11.21,15.83L9.25,13.47L6.5,17H17.5L13.96,12.29Z"/> </svg> 預(yù)覽效果 </h2> <canvas id="previewCanvas" width="1080" height="2400"></canvas> </div> <!-- 設(shè)置面板 --> <div class="settings"> <h2 class="settings-title"> <svg class="icon" viewBox="0 0 24 24"> <path d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"/> </svg> 水印設(shè)置 </h2> <div class="form-group"> <label for="imageInput">上傳圖片</label> <div class="file-upload-area" id="imageUploadArea"> <div class="file-upload-icon"> <svg viewBox="0 0 24 24" width="48" height="48" fill="currentColor"> <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/> </svg> </div> <div class="file-upload-text">拖放圖片到此處或點(diǎn)擊選擇文件</div> <div class="file-upload-button">選擇文件</div> <div class="file-name" id="imageFileName"></div> </div> <input type="file" id="imageInput" accept="image/*"> </div> <div class="form-group"> <label for="watermarkType">水印類型</label> <select id="watermarkType"> <option value="text">文字水印</option> <option value="image">圖片水印</option> </select> </div> <!-- 文字水印設(shè)置 --> <div id="textSettings" class="watermark-type active"> <div class="form-group"> <label for="watermarkText">水印文字</label> <input type="text" id="watermarkText" value="機(jī)密文件" placeholder="輸入水印文字"> </div> <div class="grid-container"> <div class="form-group"> <label for="fontSize">字體大小</label> <input type="number" id="fontSize" value="48" min="10" max="100" placeholder="字號(hào)"> </div> <div class="form-group"> <label for="textColor">文字顏色</label> <div class="range-container"> <input type="color" id="textColor" value="#cccccc"> </div> </div> </div> <div class="section"> <h3 class="section-title">高級(jí)設(shè)置</h3> <div class="form-group"> <label>旋轉(zhuǎn)角度 <span class="range-value" id="angleValue">45°</span></label> <div class="range-container"> <input type="range" id="angle" min="-180" max="180" value="45"> </div> </div> <div class="form-group"> <label>水印密度 <span class="range-value" id="densityValue">50%</span></label> <div class="range-container"> <input type="range" id="density" min="10" max="100" value="50"> </div> </div> <div class="form-group"> <label>水平間距 <span class="range-value" id="spacingXValue">150px</span></label> <div class="range-container"> <input type="range" id="spacingX" min="50" max="300" value="150"> </div> </div> <div class="form-group"> <label>垂直間距 <span class="range-value" id="spacingYValue">100px</span></label> <div class="range-container"> <input type="range" id="spacingY" min="50" max="300" value="100"> </div> </div> </div> </div> <!-- 圖片水印設(shè)置 --> <div id="imageSettings" class="watermark-type"> <div class="form-group"> <label for="watermarkImage">上傳水印圖片</label> <div class="file-upload-area" id="watermarkUploadArea"> <div class="file-upload-icon"> <svg viewBox="0 0 24 24" width="48" height="48" fill="currentColor"> <path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z"/> </svg> </div> <div class="file-upload-text">拖放水印圖片到此處或點(diǎn)擊選擇文件</div> <div class="file-upload-button">選擇圖片</div> <div class="file-name" id="watermarkFileName"></div> </div> <input type="file" id="watermarkImage" accept="image/*"> </div> <div class="form-group"> <label>縮放比例 <span class="range-value" id="scaleValue">30%</span></label> <div class="range-container"> <input type="range" id="scale" min="10" max="100" value="30"> </div> </div> </div> <div class="form-group"> <label>透明度 <span class="range-value" id="opacityValue">50%</span></label> <div class="range-container"> <input type="range" id="opacity" min="0" max="1" step="0.1" value="0.5"> </div> </div> <button id="downloadBtn"> <svg class="icon" viewBox="0 0 24 24"> <path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z"/> </svg> 下載圖片 </button> </div> </div> <script> const canvas = document.getElementById('previewCanvas'); const ctx = canvas.getContext('2d'); let originalImage = null; let watermarkImage = null; // 控件元素 const controls = { imageInput: document.getElementById('imageInput'), watermarkType: document.getElementById('watermarkType'), watermarkText: document.getElementById('watermarkText'), fontSize: document.getElementById('fontSize'), textColor: document.getElementById('textColor'), angle: document.getElementById('angle'), density: document.getElementById('density'), spacingX: document.getElementById('spacingX'), spacingY: document.getElementById('spacingY'), opacity: document.getElementById('opacity'), watermarkImageInput: document.getElementById('watermarkImage'), scale: document.getElementById('scale'), downloadBtn: document.getElementById('downloadBtn'), imageUploadArea: document.getElementById('imageUploadArea'), watermarkUploadArea: document.getElementById('watermarkUploadArea'), imageFileName: document.getElementById('imageFileName'), watermarkFileName: document.getElementById('watermarkFileName') }; // 初始化事件監(jiān)聽 function initEventListeners() { // 通用事件 controls.imageInput.addEventListener('change', handleImageUpload); controls.watermarkType.addEventListener('change', toggleWatermarkType); controls.opacity.addEventListener('input', updateRangeValue); controls.opacity.addEventListener('input', drawWatermark); // 文字水印事件 controls.watermarkText.addEventListener('input', drawWatermark); controls.fontSize.addEventListener('input', drawWatermark); controls.textColor.addEventListener('input', drawWatermark); controls.angle.addEventListener('input', updateRangeValue); controls.angle.addEventListener('input', drawWatermark); controls.density.addEventListener('input', updateRangeValue); controls.density.addEventListener('input', drawWatermark); controls.spacingX.addEventListener('input', updateRangeValue); controls.spacingX.addEventListener('input', drawWatermark); controls.spacingY.addEventListener('input', updateRangeValue); controls.spacingY.addEventListener('input', drawWatermark); // 圖片水印事件 controls.watermarkImageInput.addEventListener('change', handleWatermarkImageUpload); controls.scale.addEventListener('input', updateRangeValue); controls.scale.addEventListener('input', drawWatermark); // 下載按鈕 controls.downloadBtn.addEventListener('click', downloadImage); // 文件拖放功能 setupDragAndDrop(controls.imageUploadArea, controls.imageInput, controls.imageFileName); setupDragAndDrop(controls.watermarkUploadArea, controls.watermarkImageInput, controls.watermarkFileName); // 點(diǎn)擊上傳區(qū)域觸發(fā)文件選擇 controls.imageUploadArea.querySelector('.file-upload-button').addEventListener('click', () => controls.imageInput.click()); controls.watermarkUploadArea.querySelector('.file-upload-button').addEventListener('click', () => controls.watermarkImageInput.click()); } function setupDragAndDrop(dropArea, fileInput, fileNameDisplay) { // 阻止默認(rèn)拖放行為 ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropArea.addEventListener(eventName, preventDefaults, false); }); // 高亮顯示拖放區(qū)域 ['dragenter', 'dragover'].forEach(eventName => { dropArea.addEventListener(eventName, highlight, false); }); ['dragleave', 'drop'].forEach(eventName => { dropArea.addEventListener(eventName, unhighlight, false); }); // 處理拖放文件 dropArea.addEventListener('drop', handleDrop, false); function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } function highlight() { dropArea.classList.add('drag-over'); } function unhighlight() { dropArea.classList.remove('drag-over'); } function handleDrop(e) { const dt = e.dataTransfer; const files = dt.files; if (files.length) { fileInput.files = files; updateFileNameDisplay(files[0].name, fileNameDisplay); if (fileInput === controls.imageInput) { handleImageUpload({ target: fileInput }); } else { handleWatermarkImageUpload({ target: fileInput }); } } } } function updateFileNameDisplay(name, element) { element.textContent = name; } function updateRangeValue(e) { const target = e.target; switch(target.id) { case 'angle': document.getElementById('angleValue').textContent = `${target.value}°`; break; case 'density': document.getElementById('densityValue').textContent = `${target.value}%`; break; case 'spacingX': document.getElementById('spacingXValue').textContent = `${target.value}px`; break; case 'spacingY': document.getElementById('spacingYValue').textContent = `${target.value}px`; break; case 'opacity': document.getElementById('opacityValue').textContent = `${Math.round(target.value * 100)}%`; break; case 'scale': document.getElementById('scaleValue').textContent = `${target.value}%`; break; } } function toggleWatermarkType() { document.querySelectorAll('.watermark-type').forEach(el => { el.classList.remove('active'); }); document.getElementById(controls.watermarkType.value === 'text' ? 'textSettings' : 'imageSettings').classList.add('active'); drawWatermark(); } async function handleImageUpload(e) { const file = e.target.files[0]; if (file) { updateFileNameDisplay(file.name, controls.imageFileName); originalImage = await loadImage(file); canvas.width = originalImage.width; canvas.height = originalImage.height; drawWatermark(); } } async function handleWatermarkImageUpload(e) { const file = e.target.files[0]; if (file) { updateFileNameDisplay(file.name, controls.watermarkFileName); watermarkImage = await loadImage(file); drawWatermark(); } } function loadImage(file) { return new Promise((resolve) => { const reader = new FileReader(); reader.onload = (e) => { const img = new Image(); img.onload = () => resolve(img); img.src = e.target.result; }; reader.readAsDataURL(file); }); } function drawWatermark() { if (!originalImage) return; ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(originalImage, 0, 0); ctx.globalAlpha = controls.opacity.value; if (controls.watermarkType.value === 'text') { drawTextWatermark(); } else if (watermarkImage) { drawImageWatermark(); } ctx.globalAlpha = 1.0; } function drawTextWatermark() { ctx.save(); ctx.font = `${controls.fontSize.value}px Arial`; ctx.fillStyle = controls.textColor.value; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; const rotation = (controls.angle.value * Math.PI) / 180; // 根據(jù)密度調(diào)整間距 const densityFactor = 1 + (100 - controls.density.value) / 50; const stepX = parseInt(controls.spacingX.value) * densityFactor; const stepY = parseInt(controls.spacingY.value) * densityFactor; // 創(chuàng)建平鋪效果 for (let x = -canvas.width; x < canvas.width * 2; x += stepX) { for (let y = -canvas.height; y < canvas.height * 2; y += stepY) { ctx.save(); ctx.translate(x, y); ctx.rotate(rotation); ctx.fillText(controls.watermarkText.value, 0, 0); ctx.restore(); } } ctx.restore(); } function drawImageWatermark() { const scale = controls.scale.value / 100; const width = watermarkImage.width * scale; const height = watermarkImage.height * scale; ctx.save(); // 右下角位置 const x = canvas.width - width - 20; const y = canvas.height - height - 20; ctx.drawImage(watermarkImage, x, y, width, height); ctx.restore(); } function downloadImage() { if (!originalImage) { alert('請(qǐng)先上傳圖片'); return; } const link = document.createElement('a'); link.download = `watermarked-${Date.now()}.png`; link.href = canvas.toDataURL('image/png'); link.click(); } // 初始化 initEventListeners(); updateRangeValue({ target: controls.opacity }); updateRangeValue({ target: controls.scale }); updateRangeValue({ target: controls.density }); // 添加默認(rèn)背景 ctx.fillStyle = '#f5f5f5'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#999'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.font = '24px Arial'; ctx.fillText('上傳圖片后預(yù)覽效果將顯示在這里', canvas.width/2, canvas.height/2); </script> </body> </html>
總結(jié)
這篇文章,我們不僅展示了一個(gè)現(xiàn)代化、UI 優(yōu)雅、功能強(qiáng)大的前端圖片水印工具的開發(fā)案例,更結(jié)合了 HTML5 + CSS3 的最佳實(shí)踐,為大家提供了一個(gè)可以即用、也能深度定制的模板基礎(chǔ)。
不論你是:
想保護(hù)自己原創(chuàng)作品的內(nèi)容創(chuàng)作者,
想為公司運(yùn)營提供內(nèi)容加密的美工同事,
還是正在學(xué)習(xí)前端開發(fā)的工程師,
都能從這款工具中找到價(jià)值,并快速上手開發(fā)屬于自己的版本!
到此這篇關(guān)于JS+HTML實(shí)現(xiàn)在線圖片水印添加工具的文章就介紹到這了,更多相關(guān)JS圖片水印內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
理解Python數(shù)據(jù)離散化手寫if-elif語句與pandas中cut()方法實(shí)現(xiàn)
這篇文章主要介紹了通過手寫if-elif語句與pandas中cut()方法實(shí)現(xiàn)示例理解Python數(shù)據(jù)離散化詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05python多進(jìn)程 主進(jìn)程和子進(jìn)程間共享和不共享全局變量實(shí)例
這篇文章主要介紹了python多進(jìn)程 主進(jìn)程和子進(jìn)程間共享和不共享全局變量實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-04-04python?aeon庫進(jìn)行時(shí)間序列算法預(yù)測(cè)分類實(shí)例探索
這篇文章主要介紹了python?aeon庫進(jìn)行時(shí)間序列算法預(yù)測(cè)分類實(shí)例探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-02-02pandas DataFrame 數(shù)據(jù)選取,修改,切片的實(shí)現(xiàn)
這篇文章主要介紹了pandas DataFrame 數(shù)據(jù)選取,修改,切片的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04jupyter使用自動(dòng)補(bǔ)全和切換默認(rèn)瀏覽器的方法
這篇文章主要介紹了jupyter使用自動(dòng)補(bǔ)全和切換默認(rèn)瀏覽器的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11PyQt5主窗口動(dòng)態(tài)加載Widget實(shí)例代碼
這篇文章主要介紹了PyQt5主窗口動(dòng)態(tài)加載Widget實(shí)例代碼,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02Python基礎(chǔ)常用內(nèi)建函數(shù)圖文示例解析
這篇文章主要為大家Python常用內(nèi)建函數(shù),文中通過圖例詳細(xì)的給大家作出了講解分析,有需要的朋友可以借鑒參考下,希望可以有所幫助2021-09-09