目录
📁 功能介绍
上传任意图片海报
自动添加水印(文字 + 网格)图层
使用原生
<canvas>
,无依赖,适合本地项目
📁 项目结构
watermark-demo/
├── index.html
├── package.json
├── vite.config.js
├── src/
│ ├── main.js
│ ├── App.vue
│ └── components/
│ └── WatermarkOverlay.vue
📁 快速搭建
npm init vite@latest
# 选择 vue -> 名称为 "watermark-demo"
cd watermark-demo
npm install
App.vue
<template>
<div class="app">
<h1>🖼️ 海报上传 + 水印演示</h1>
<input type="file" accept="image/*" @change="onUpload" />
<div
v-if="imageUrl"
class="poster-container"
:style="{ width: `${imgWidth}px`, height: `${imgHeight}px` }"
>
<img
:src="imageUrl"
:width="imgWidth"
:height="imgHeight"
@load="onImageLoad"
/>
<WatermarkOverlay
v-if="imgWidth && imgHeight"
:width="imgWidth"
:height="imgHeight"
text="稿定"
/>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import WatermarkOverlay from './components/WatermarkOverlay.vue'
const imageUrl = ref(null)
const imgWidth = ref(0)
const imgHeight = ref(0)
const onUpload = (e) => {
const file = e.target.files[0]
if (file) {
imageUrl.value = URL.createObjectURL(file)
}
}
const onImageLoad = (e) => {
imgWidth.value = e.target.naturalWidth
imgHeight.value = e.target.naturalHeight
}
</script>
<style scoped>
.app {
padding: 2rem;
}
.poster-container {
position: relative;
border: 1px solid #ddd;
margin-top: 1rem;
display: inline-block;
}
</style>
main.js
import { createApp } from 'vue'
import App from './App.vue'
import './style.css'
createApp(App).mount('#app')
WatermarkOverlay.vue
<template>
<canvas ref="canvas" class="watermark-canvas" />
</template>
<script setup>
import { onMounted, ref, watch } from 'vue'
const props = defineProps({
width: Number,
height: Number,
text: {
type: String,
default: '稿定'
}
})
const canvas = ref(null)
const drawWatermark = () => {
const el = canvas.value
if (!el) return
const ctx = el.getContext('2d')
el.width = props.width
el.height = props.height
ctx.clearRect(0, 0, el.width, el.height)
const spacing = 250
const lineColor = 'rgba(255,255,255,0.3)'
const textColor = 'rgba(255,255,255,0.3)'
ctx.lineWidth = 1
ctx.strokeStyle = lineColor
// 画斜线(左上到右下)
for (let x = -el.height; x < el.width + el.height; x += spacing) {
ctx.beginPath()
ctx.moveTo(x, 0)
ctx.lineTo(x - el.height, el.height)
ctx.stroke()
}
// 画斜线(右上到左下)
for (let x = 0; x < el.width + el.height; x += spacing) {
ctx.beginPath()
ctx.moveTo(x, 0)
ctx.lineTo(x + el.height, el.height)
ctx.stroke()
}
// 画文字水印
ctx.save()
ctx.font = '24px sans-serif'
ctx.fillStyle = textColor
ctx.rotate(-Math.PI / 4)
for (let x = -el.width; x < el.width * 2; x += spacing * 1.5) {
for (let y = -el.height; y < el.height * 2; y += spacing * 1.5) {
ctx.fillText(props.text, x, y)
}
}
ctx.restore()
}
onMounted(drawWatermark)
watch(() => [props.width, props.height, props.text], drawWatermark)
</script>
<style scoped>
.watermark-canvas {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
z-index: 10;
}
</style>
🚀 启动项目
npm install
npm run dev
打开浏览器访问:http://localhost:5173,上传一张图片,并看到实时水印叠加效果了。