Skip to content

Commit

Permalink
feat: 新增圖片添加浮水印
Browse files Browse the repository at this point in the history
  • Loading branch information
yuna9068 committed Mar 12, 2021
0 parents commit 1d33b3c
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
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 added favicon.ico
Binary file not shown.
49 changes: 49 additions & 0 deletions index.html
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>
121 changes: 121 additions & 0 deletions main.css
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);}
}
161 changes: 161 additions & 0 deletions main.js
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';
}

0 comments on commit 1d33b3c

Please sign in to comment.