-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1d33b3c
Showing
5 changed files
with
340 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# 圖片添加浮水印 | ||
|
||
|
||
## 網頁主要功能及特色 | ||
* 圖片添加浮水印資訊:檔名、檔案大小、圖片寬高 | ||
|
||
|
||
## 使用到的技術、工具 | ||
* [Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API "Canvas API - Web APIs | MDN"):HTML5 元素 |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<!DOCTYPE html> | ||
<html lang="zh-Hant-TW"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | ||
<link rel="shortcut icon" href="favicon.ico"> | ||
<link rel="stylesheet" href="main.css"> | ||
<title>圖片添加浮水印</title> | ||
</head> | ||
<body> | ||
<div class="main"> | ||
<section> | ||
<div> | ||
<label for="selectImg" class="btn">選擇圖片</label> | ||
<input | ||
id="selectImg" | ||
type="file" | ||
accept="image/*" | ||
onchange="getImg(this)" | ||
onclick="this.value=null;" | ||
> | ||
</div> | ||
<div> | ||
<img class="originalPhoto"> | ||
</div> | ||
</section> | ||
<section> | ||
<div> | ||
<button class="btn btnDownload"> | ||
<a class="downloadLink" download='watermark.png'>下載添加浮水印的圖片</a> | ||
</button> | ||
</div> | ||
<div> | ||
<img class="watermarkImg"> | ||
</div> | ||
</section> | ||
</div> | ||
|
||
<div class="loading"> | ||
<div class="bubbleBlock"> | ||
<div class="bubble"></div> | ||
<div class="bubble"></div> | ||
</div> | ||
</div> | ||
|
||
<script src="main.js"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
* { | ||
box-sizing: border-box; | ||
margin: 0; | ||
} | ||
|
||
a { | ||
text-decoration: none; | ||
color: inherit; | ||
} | ||
|
||
/* src 為空值或沒有 src 屬性,避免在畫面上顯示預設外框 */ | ||
img[src=""], img:not([src]), .btnDownload { | ||
opacity: 0; | ||
} | ||
|
||
img { | ||
width: auto; | ||
height: auto; | ||
max-width: 100%; | ||
max-height: 100%; | ||
} | ||
|
||
label + input { | ||
display: none; | ||
} | ||
|
||
section { | ||
padding: 1rem; | ||
text-align: center; | ||
} | ||
|
||
section:first-child { | ||
width: 30%; | ||
} | ||
|
||
section:last-child { | ||
width: 70%; | ||
} | ||
|
||
@media ( max-width: 414px ) { | ||
section:first-child, section:last-child { | ||
width: 100%; | ||
} | ||
} | ||
|
||
.main { | ||
display: flex; | ||
flex-wrap: wrap; | ||
} | ||
|
||
.btn { | ||
display: inline-block; | ||
padding: 10px; | ||
border: 2px solid #007AFF; | ||
outline: none; | ||
background: #ffffff; | ||
color: #007AFF; | ||
margin-bottom: 1rem; | ||
font-size: 1rem; | ||
letter-spacing: 1px; | ||
cursor: pointer; | ||
transition: all 0.2s; | ||
} | ||
|
||
.btn:hover { | ||
border-radius: 30px; | ||
transition: all 0.2s; | ||
} | ||
|
||
.loading { | ||
width: 100%; | ||
height: 100%; | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
text-align: center; | ||
z-index: 99; | ||
background: rgba(0, 0, 0, 0.5); | ||
color: #ffffff; | ||
font-size: 30px; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
display: none; | ||
} | ||
|
||
.bubbleBlock { | ||
width:60px; | ||
height:60px; | ||
animation: bubbleRotate 2s linear infinite normal both; | ||
} | ||
|
||
.bubble { | ||
display:inline-block; | ||
position: absolute; | ||
top: 0; | ||
width:20px; | ||
height:20px; | ||
border-radius:50%; | ||
background-color:#87cefa; | ||
} | ||
|
||
.bubble:nth-child(1){ | ||
animation: bubbleScale 2s 0s infinite alternate both; | ||
} | ||
|
||
.bubble:nth-child(2){ | ||
top: auto; | ||
bottom: 0; | ||
animation: bubbleScale 2s 1s infinite alternate both; | ||
} | ||
|
||
@keyframes bubbleScale { | ||
0%{transform:scale(0, 0);} | ||
100%{transform:scale(1, 1);} | ||
} | ||
@keyframes bubbleRotate { | ||
0%{transform: rotate(0deg);} | ||
|
||
100%{transform: rotate(360deg);} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
/** | ||
* 取得選擇的檔案及相關資訊 | ||
* | ||
* @param {Object} e - DOM | ||
*/ | ||
function getImg(e) { | ||
showLoading(); | ||
|
||
initialization(); | ||
|
||
const selectFile = e.files[0]; // 選擇的檔案 | ||
// console.log('檔案來源路徑', e.value); | ||
// console.log('檔案類型', selectFile.type); | ||
// console.log('檔案最後修改時間', selectFile.lastModifiedDate); | ||
|
||
if (selectFile) { | ||
let fileReader = new FileReader(); | ||
fileReader.readAsDataURL(selectFile); | ||
|
||
// 取得檔案 | ||
fileReader.onload = () => { | ||
// 顯示原始圖片 | ||
document.querySelector('.originalPhoto').src = fileReader.result; | ||
|
||
const imgInstance = new Image(); | ||
imgInstance.src = fileReader.result; | ||
imgInstance.onload = () => { | ||
// 原始圖片資訊 | ||
const photoInfo = [{ | ||
name: '檔名', | ||
value: selectFile.name, | ||
},{ | ||
name: '檔案大小', | ||
value: `${selectFile.size} byte`, | ||
},{ | ||
name: '圖片寬高', | ||
value: `${imgInstance.width} * ${imgInstance.height}`, | ||
}]; | ||
|
||
addWatermark(imgInstance, photoInfo); | ||
} | ||
} | ||
} else { | ||
hideLoading(); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* 加上浮水印,注意繪製順序,先畫圖片,再來是文字背景區塊,最後才是文字 | ||
* | ||
* @param {Object} img - 圖片實例 | ||
* @param {Array} info - 圖片資訊 | ||
*/ | ||
function addWatermark(img, info) { | ||
if (!img) { | ||
hideLoading(); | ||
return; | ||
} | ||
|
||
const imgWidth = img.width; // 圖片寬度 | ||
const imgHeight = img.height; // 圖片高度 | ||
|
||
// 浮水印文字區塊 | ||
let textBlockHeight = imgHeight * 0.3; // 文字區塊高度 | ||
if (imgWidth < imgHeight) { // 直式照片 | ||
textBlockHeight = imgHeight * 0.25; | ||
} | ||
const textBlockRowHeight = textBlockHeight / info.length; // 文字區塊每行高度 | ||
const fontSize = (textBlockRowHeight / 1.2) / 1.375; // 文字大小 | ||
const textBlockPaddingX = fontSize; // 文字區塊左右 padding 距離 | ||
const textBlockPaddingY = fontSize; // 文字區塊上下 padding 距離 | ||
|
||
// 浮水印文字區塊背景 | ||
const textBackgroundHeight = textBlockHeight + (textBlockPaddingY * 2); // 浮水印文字區塊背景高度 | ||
|
||
// 設定畫布 | ||
let canvas = document.createElement('canvas'); | ||
let ctx = canvas.getContext('2d'); | ||
canvas.width = imgWidth; | ||
canvas.height = imgHeight; | ||
|
||
// 繪製原始圖片 | ||
ctx.drawImage(img, 0, 0, imgWidth, imgHeight); | ||
|
||
// 繪製浮水印文字區塊背景 | ||
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'; // 浮水印文字區塊背景色 | ||
ctx.fillRect(0, (imgHeight - textBackgroundHeight), imgWidth, textBackgroundHeight); | ||
|
||
// 繪製浮水印文字區塊 | ||
ctx.font = `${fontSize}px sans-serif`; | ||
ctx.fillStyle = '#ffffff'; // 文字顏色 | ||
|
||
info.forEach((item, i) => { | ||
const fillStr = measureTextWidth(ctx, imgWidth, `${item.name}:${item.value}`, fontSize); | ||
// const fillStartHeight = imgHeight - textBackgroundHeight + textBlockPaddingY + (textBlockRowHeight * i); | ||
const fillStartHeight = imgHeight - textBackgroundHeight + (textBlockPaddingY * 2) + (textBlockRowHeight * i); | ||
ctx.fillText(fillStr, textBlockPaddingX, fillStartHeight); | ||
}); | ||
|
||
const watermarkImg = canvas.toDataURL(); | ||
document.querySelector('.watermarkImg').src = watermarkImg; | ||
document.querySelector('.downloadLink').href = watermarkImg; | ||
document.querySelector('.btnDownload').style.opacity = 1; | ||
|
||
hideLoading(); | ||
} | ||
|
||
|
||
/** | ||
* 設定顯示文字 | ||
* | ||
* @param {Object} ctx - 繪圖環境 | ||
* @param {number} imgWidth - 圖片寬度 | ||
* @param {string} text - 文字內容 | ||
* @param {number} fontSize - 字體大小 | ||
* @returns {string} displayText - 要繪製到圖片上的文字內容 | ||
*/ | ||
function measureTextWidth(ctx, imgWidth, text, fontSize) { | ||
let displayText = text; | ||
let textWidth = ctx.measureText(displayText).width; | ||
let totalWidth = textWidth + (fontSize * 2); | ||
|
||
while (totalWidth > imgWidth) { | ||
displayText = displayText.slice(0, displayText.length - 1); | ||
textWidth = ctx.measureText(displayText).width; | ||
totalWidth = textWidth + (fontSize * 2); | ||
} | ||
|
||
if (displayText !== text) { | ||
displayText = `${displayText.slice(0, displayText.length - 1)}...`; | ||
} | ||
return displayText; | ||
} | ||
|
||
|
||
/** | ||
* 清空畫面資料 | ||
*/ | ||
function initialization() { | ||
document.querySelector('.originalPhoto').src = ''; | ||
document.querySelector('.watermarkImg').src = ''; | ||
document.querySelector('.btnDownload').style.opacity = 0; | ||
document.querySelector('.downloadLink').href = ''; | ||
} | ||
|
||
|
||
/** | ||
* 顯示 loading | ||
*/ | ||
function showLoading() { | ||
document.querySelector('.loading').style.display = 'flex'; | ||
} | ||
|
||
|
||
/** | ||
* 隱藏 loading | ||
*/ | ||
function hideLoading() { | ||
document.querySelector('.loading').style.display = 'none'; | ||
} |