Demo: 给图片添加自定义水印并下载

给图片添加自定义水印并下载

javascript 复制代码
<template>
  <div class="wrap">
    <div class="optea">
      <p>水印文字</p>
      <el-input v-model="watermarkOptions.text" placeholder="请输入水印内容"></el-input>
      <p>字体颜色</p>
      <el-color-picker v-model="watermarkOptions.color"></el-color-picker>
      <p>旋转角度</p>
      <el-slider v-model="watermarkOptions.rotate" :min="0" :max="360"></el-slider>
      <p>透明度</p>
      <el-slider v-model="watermarkOptions.alpha" :min="0" :max="100"></el-slider>
      <p>文本间距</p>
      <el-slider v-model="watermarkOptions.width" :min="0" :max="100"></el-slider>
      <p>字体大小</p>
      <el-slider v-model="watermarkOptions.fontSize" :min="0" :step="0.5" :max="50"></el-slider>
    </div>
    <div class="previewImage">
      <el-button
        @click="handleDown"
        class="download-btn"
        type="primary"
        plain
        icon="el-icon-download"
        >下载水印图片</el-button
      >
      <div class="preview" ref="previewImg">
        <img :src="preImg" alt="" />
        <div class="watermark" :style="{ background: paint }"></div>
      </div>
    </div>
  </div>
</template>
<script setup>
import DomToImage from 'dom-to-image'
import { ref, reactive, computed } from 'vue'

const previewImg = ref(null)
const watermarkOptions = reactive({
  text: '仅供 xxx 验证使用',
  fontSize: 10,
  width: 5,
  color: '#000000',
  alpha: 35,
  rotate: 35
})

defineProps({
  preImg: {
    type: String,
    default: ''
  }
})

const handleDown = () => {
  let node = previewImg.value
  DomToImage.toPng(node)
    .then(function (dataUrl) {
      var oLink = document.createElement('a')
      oLink.download = '水印图片.png'
      oLink.href = dataUrl
      oLink.click()
    })
    .catch(function (error) {
      console.error('oops, something went wrong!', error)
    })
}

const paint = computed(() => {
  const wordWidth = watermarkOptions.fontSize * watermarkOptions.text.split('').length
  const width = wordWidth + watermarkOptions.width

  let svgText = `
    <svg xmlns="http://www.w3.org/2000/svg" width="${width}px" height="${width}px">
    <text x="50%" y="50%"
        alignment-baseline="middle"
        text-anchor="middle"
        stroke="${watermarkOptions.color}"
        opacity="${watermarkOptions.alpha / 100}"
        transform="translate(${width / 2}, ${width / 2}) rotate(${
          watermarkOptions.rotate
        }) translate(-${width / 2}, -${width / 2})"
        font-weight="100"
        font-size="${watermarkOptions.fontSize}"
        font-family="microsoft yahe"
        >
        ${watermarkOptions.text}
    </text>
   </svg>`
  return `url(data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svgText)))})`
})
</script>

<style lang="scss" scoped>
.wrap {
  padding: 10px 20px;
  display: flex;
  justify-content: flex-start;
  align-items: center;

  .optea {
    // width: 500px;
    margin-right: 20px;

    p {
      margin: 5px 0;
      margin-bottom: 10px;
      font-weight: 600;
    }
  }

  .previewImage {
    .download-btn {
      margin: 0 15px 15px;
    }

    .preview {
      position: relative;
      min-width: 520px;
      max-height: 800px;

      img {
        width: 100%;
        max-height: 800px;
      }

      .watermark {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
      }
    }
  }
}
</style>
相关推荐
敲敲敲敲暴你脑袋14 分钟前
写个添加注释的vscode插件
javascript·typescript·visual studio code
SuperEugene1 小时前
后台权限与菜单渲染:基于路由和后端返回的几种实现方式
前端·javascript·vue.js
csdn飘逸飘逸1 小时前
Autojs基础-全局函数与变量(globals)
javascript
KKKK1 小时前
手写Promise,从测试用例的角度理解
javascript
青青家的小灰灰1 小时前
迈向全栈新时代:SSR/SSG 原理、Next.js 架构与 React Server Components (RSC) 实战
前端·javascript·react.js
SuperEugene1 小时前
弹窗与抽屉组件封装:如何做一个全局可控的 Dialog 服务
前端·javascript·vue.js
青青家的小灰灰1 小时前
透视 React 内核:Diff 算法、合成事件与并发特性的深度解析
前端·javascript·react.js
北冥有鱼1 小时前
JSON或代码对比的工具-vue
vue.js
SuperEugene1 小时前
组合式函数 、 Hooks(Vue2 mixin 、 Vue3 composables)的实战封装
前端·javascript·vue.js
wuhen_n1 小时前
模板编译三阶段:parse-transform-generate
前端·javascript·vue.js