当前位置:   article > 正文

vue3网页端屏幕截图并可以裁剪,反转,添加批注等_vue3 截图

vue3 截图

实现网页版的屏幕截图添加备注等功能,效果如下:
在这里插入图片描述
需要安装插件:tui-image-editor
这个弹出框具体代码:

<template>
  <!-- 屏幕截图弹出窗 -->
  <el-dialog
    v-model="dialogShow"
    custom-class="customSnapshotDialog"
    title="调整截图"
    width="1000px"
    top="49px"
    :close-on-click-modal="false"
    :destroy-on-close="true"
    @close="handleClose"
  >
    <div class="boardBox">
      <div id="tui-image-editor" />
    </div>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="handleClose">取 消</el-button>
        <el-button type="primary" @click="handleCanvas2Img">确认上传</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script>
import { reactive, toRefs, watch, nextTick } from 'vue'
import 'tui-image-editor/dist/tui-image-editor.css'
import 'tui-color-picker/dist/tui-color-picker.css'

export default {
  name: 'DialogPageSnapshot',
  components: { },
  props: {
    dialogVisiable: {
      type: Boolean,
      default: false
    },
    imgUrl: {
      type: String,
      default: ''
    }
  },
  emits: ['closeDialog'],
  setup(props, context) {
    // const { proxy } = getCurrentInstance()
    const state = reactive({
      dialogShow: false,
      imgBaseUrl: '',
      // 创建的画布对象
      instance: null,
      ImageEditor: require('tui-image-editor'),
      localeCN: {
        Crop: '裁剪',
        Draw: '涂鸦',
        Text: '添加文本',
        Undo: '上一步',
        Redo: '下一步',
        Reset: '重置',
        Apply: '确定',
        Cancel: '取消',
        Custom: '自定义',
        Square: '正方形',
        Free: '曲线',
        Straight: '直线',
        Color: '颜色',
        Range: '粗细/角度',
        Bold: '加粗',
        Italic: '斜体',
        Underline: '下划线',
        Left: '左对齐',
        Center: '居中',
        Right: '右对齐',
        'Flip X': 'X 轴',
        'Flip Y': 'Y 轴',
        Flip: '镜像',
        Rotate: '旋转',
        ZoomIn: '放大',
        ZoomOut: '缩小',
        Hand: '拖动',
        'Text size': '字体大小'
      },
      customTheme: {
        // image 坐上角度图片
        'common.bi.image': '', // 在这里换上你喜欢的logo图片
        'common.bisize.width': '0px',
        'common.bisize.height': '0px',
        'common.backgroundImage': 'none',
        'common.backgroundColor': '#f3f4f6',
        'common.border': 'none',

        // header
        'header.backgroundImage': 'none',
        'header.backgroundColor': 'transparent',
        'header.border': '0px',

        // load button
        'loadButton.backgroundColor': '#fff',
        'loadButton.border': '1px solid #ddd',
        'loadButton.color': '#222',
        'loadButton.fontFamily': 'NotoSans, sans-serif',
        'loadButton.fontSize': '12px',
        'loadButton.display': 'none', // 可以直接隐藏掉

        // download button
        'downloadButton.backgroundColor': '#fdba3b',
        'downloadButton.border': '1px solid #fdba3b',
        'downloadButton.color': '#fff',
        'downloadButton.fontFamily': 'NotoSans, sans-serif',
        'downloadButton.fontSize': '12px',
        'downloadButton.display': 'none', // 可以直接隐藏掉

        // icons default
        'menu.normalIcon.color': '#8a8a8a',
        'menu.activeIcon.color': '#555555',
        'menu.disabledIcon.color': '#434343',
        'menu.hoverIcon.color': '#e9e9e9',
        'submenu.normalIcon.color': '#8a8a8a',
        'submenu.activeIcon.color': '#e9e9e9',

        'menu.iconSize.width': '18px',
        'menu.iconSize.height': '18px',
        'submenu.iconSize.width': '20px',
        'submenu.iconSize.height': '20px',
        // 'submenu.backgroundColor': '#ddd',

        // submenu primary color
        // 'submenu.backgroundColor': 'currentcolor',
        'submenu.partition.color': '#858585',

        // submenu labels
        'submenu.normalLabel.color': '#858585',
        'submenu.normalLabel.fontWeight': 'lighter',
        'submenu.activeLabel.color': '#fff',
        'submenu.activeLabel.fontWeight': 'lighter',

        // checkbox style
        'checkbox.border': '1px solid #ccc',
        'checkbox.backgroundColor': '#fff',

        // rango style
        'range.pointer.color': '#fff',
        'range.bar.color': '#666',
        'range.subbar.color': '#d1d1d1',

        'range.disabledPointer.color': '#414141',
        'range.disabledBar.color': '#282828',
        'range.disabledSubbar.color': '#414141',

        'range.value.color': '#fff',
        'range.value.fontWeight': 'lighter',
        'range.value.fontSize': '11px',
        'range.value.border': '1px solid #353535',
        'range.value.backgroundColor': '#151515',
        'range.title.color': '#fff',
        'range.title.fontWeight': 'lighter',

        // colorpicker style
        // 'colorpicker.button.border': '1px solid #1e1e1e',
        'colorpicker.title.color': '#fff'
      }
    })

    watch(props, (newValue) => {
      state.dialogShow = newValue.dialogVisiable
      if (state.dialogShow) {
        state.refPage = newValue.refPage
        state.imgBaseUrl = newValue.imgUrl
        if (state.imgBaseUrl) {
          nextTick(() => {
            // 获取到当前屏幕的宽高,用于判断当前是大屏幕还是小屏幕==》进而确定的那个要渲染哪个init(画布大小不一样)
            // 如果不压缩图片大小,太大的图片出现拖动,手机端无法操作,所以要根据不同屏幕大小渲染不同大小的画布
            if (document.documentElement.clientWidth <= 500) {
              initMini()
            } else {
              // 页面加载好,就调用这个方法来创建图片编辑器
              init()
            }
          })
        }
      }
    })
    const init = () => {
      // 创建tui-image-editor组件实例,后续操作需要用到this.instance这个对象
      state.instance = new state.ImageEditor(document.querySelector('#tui-image-editor'), {
        includeUI: {
          // 默认加载的图片
          loadImage: {
            // 图片路径
            path: state.imgBaseUrl,
            // 图片的名字,可以省略
            name: 'image'
          },
          // 默认开启绘图的功能,小屏幕情况下,直接打开菜单,会占用较大屏幕空间,不美观
          initMenu: 'draw',
          // 支持的菜单
          menu: [
            'crop', // 裁切
            'draw', // 添加绘画
            'text', // 添加文本
            'rotate', // 旋转
            'flip' // 翻转
            // 'shape', // 添加形状
            // 'icon', // 添加图标
            // 'mask', // 添加覆盖
            // 'filter' // 添加滤镜
          ],
          // 菜单位置在下面
          menuBarPosition: 'bottom',
          // 汉化
          locale: state.localeCN,
          // 自定义样式(隐藏默认顶部栏目、按钮颜色。。。)
          theme: state.customTheme
        },
        // 设置画布的最大宽高,能自动等比例缩放大图片到指定的宽高内
        // TODO:可以监听当前页面的缩放,动态修改画布最大宽高以防止图片过大
        cssMaxWidth: 850
        // cssMaxHeight: 580
      }
      )
      // 清除自定义样式造成的一条边框线
      document.getElementsByClassName('tui-image-editor-main')[0].style.top = 0
      // 你也可以指定那个菜单隐藏,留几个有用的菜单
      // document.querySelector('[tooltip-content="Undo"]').style.display = 'none'// 上一步
      // document.querySelector('[tooltip-content="Redo"]').style.display = 'none' // 下一步
      // document.querySelector('[tooltip-content="Reset"]').style.display = 'none' // 完全重新编辑
      // document.querySelector('[tooltip-content="ZoomIn"]').style.display = 'none' // 放大
      // document.querySelector('[tooltip-content="ZoomOut"]').style.display = 'none' // 缩小
      // document.querySelector('[tooltip-content="Hand"]').style.display = 'none' // 拖动界面
      document.querySelector('[tooltip-content="History"]').style.display = 'none'
      document.querySelector('[tooltip-content="Delete"]').style.display = 'none' // 删除选中编辑内容
      document.querySelector('[tooltip-content="DeleteAll"]').style.display = 'none' // 清空
      // 隐藏分割线
      document.querySelectorAll('.tui-image-editor-icpartition').forEach(item => {
        item.style.display = 'none'
      })
    }
    // 创建图片编辑器 ==>小屏幕
    const initMini = () => {
      // 创建tui-image-editor组件实例,后续操作需要用到this.instance这个对象
      state.instance = new state.ImageEditor(
        document.querySelector('#tui-image-editor'),
        {
          includeUI: {
            // 默认加载的图片
            loadImage: {
              // 图片路径
              path: state.imgBaseUrl,
              // 图片的名字,可以省略
              name: 'image'
            },
            // 默认开启绘图的功能,小屏幕情况下,直接打开菜单,会占用较大屏幕空间,不美观
            initMenu: 'draw',
            // 支持的菜单
            menu: [
              'crop', // 裁切
              'draw', // 添加绘画
              'text' // 添加文本
            ],
            // 菜单位置在下面
            menuBarPosition: 'bottom',
            // 汉化
            locale: state.localeCN,
            // 自定义样式(隐藏默认顶部栏目、按钮颜色。。。)
            theme: state.customTheme
          },
          // 设置画布的最大宽高,能自动等比例缩放大图片到指定的宽高内
          // !设置小图宽高,自动压缩图片,防止过大出现滚动,导致无法操作
          cssMaxWidth: 350,
          cssMaxHeight: 500
        }
      )
      // 清除自定义样式造成的一条边框线
      document.getElementsByClassName('tui-image-editor-main')[0].style.top = 0
      // 设置图片编辑其余距离底部90px(就不会被底部展开的工具栏遮挡住了)===>无效
      // document.getElementsByClassName('tui-image-editor-wrap')[0].style.bottom = 90

      //! 修改图片编辑器的顶部导航栏
      // document.querySelector('[tooltip-content="Undo"]').style.display = 'none'// 上一步
      document.querySelector('[tooltip-content="History"]').style.display = 'none'
      document.querySelector('[tooltip-content="Delete"]').style.display = 'none' // 删除选中编辑内容
      document.querySelector('[tooltip-content="DeleteAll"]').style.display = 'none' // 清空
      // 隐藏分割线
      document.querySelectorAll('.tui-image-editor-icpartition').forEach(item => {
        item.style.display = 'none'
      })
    }
    /** 保存编辑后图片 */
    const handleCanvas2Img = () => {
      // 要延时调用,否则会被锁死,因为异步方法会在没有这个dom时触发它
      setTimeout(() => {
        // state.instance.toDataURL() 编辑后的base64图片码
        // 传给上传组件(父组件)
        context.emit('closeDialog', state.instance.toDataURL())
      }, 700)
    }
    // 将base64转换成file类型,用于给tui-image-editor组件的官方方法调用,计算获得当前图片的宽高
    // ? this.instance.loadImageFromFile这个官方的方法会返回图片的宽高,但是传入的必须时file类型的文件
    const dataURLtoFile = (dataurl) => {
      var arr = dataurl.split(',')
      var mime = arr[0].match(/:(.*?);/)[1]
      var bstr = atob(arr[1])
      var n = bstr.length
      var u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new File([u8arr], { type: mime })
    }

    const handleClose = () => {
      state.dialogShow = false
      context.emit('closeDialog', false)
    }

    return { ...toRefs(state), handleClose, handleCanvas2Img, dataURLtoFile, initMini, init }
  }
}
</script>
<style lang="scss" scoped>
.boardBox {
  width: 100%;
  height: 79.5vh;
  background: #f9f9f9;
}

// 弹窗的关闭按钮
.closeBigBtn {
  position: absolute;
  left: 150px;
  top: 12px;
}
</style>

<style lang="scss">
.tui-image-editor-container .tui-image-editor-controls {
  height: 40px;
  background-color: #ddd;
}
.tui-image-editor-container .tui-image-editor-wrap {
  padding: 6px 0;
  position: initial;
  flex: 1;
}
.tui-image-editor-container .tui-image-editor-main {
  display: flex;
  flex-direction: column-reverse;
}
.tui-image-editor-container .tui-image-editor-main-container {
  height: calc(100% - 40px);
}
.tui-image-editor-container .color-picker-value {
  width: 22px;
  height: 22px;
}
.tui-image-editor {
  top: 0 !important;
}
.tui-image-editor-submenu {
  border-radius: 10px 10px 0 0 !important;
}
.tui-image-editor-container .tui-image-editor-partition > div {
  height: 40px;
}

.tui-image-editor-submenu-item {
  padding: 8px 0 8px 0 !important;
}
.tui-image-editor-container {
  background-color: transparent;
}
.tui-image-editor-container li {
  line-height: initial;
}
.tui-image-editor-container .svg_ic-submenu {
  display: inline-block;
}
/* 强制压缩菜单的高度 ,减少占用屏幕的空间*/
.tui-image-editor-container .tui-image-editor-submenu {
  height: auto !important;
  position: sticky !important;
}

.tui-image-editor-container.bottom .tui-image-editor-submenu>div {
  padding: 0 !important;
}

/* 顶部工具栏定位 */
.tui-image-editor-container .tui-image-editor-header {
  top: -55px;
}
.tui-image-editor-container .tui-image-editor-help-menu {
  border-radius: 10px 10px 0 0;
}
.tui-image-editor-container .tui-image-editor-help-menu.top {
  top: -32px;
  background-color: #f0f2f5;
  height: auto;
}
/* 顶部工具栏定位 */
.tui-image-editor-container {
  overflow: visible;
}</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403

父组件

<!-- 调整截图弹出框 -->
<DialogPageSnapshot :dialog-visiable="dialogSnapshot" :img-url="imgBaseUrl" @closeDialog="handleCloseSnapshot" />
  • 1
  • 2
import html2canvas from 'html2canvas'

// 屏幕快照
async function PageSnapshot(ele) {
  let url = ''
  await html2canvas(ele, {
    dpi: 300,
    scale: 2, // 处理图片模糊
    background: '#fff',
    useCORS: true // 允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。
  }).then((canvas) => {
    url = canvas.toDataURL('image/jpg')
  })
  return url
}
// 点击弹出框显示的事件内容
const handleDialogClick = () => {
	// 需要打印的模块
	const elementHtml = document.getElementsByClassName('box-excel')[0]
	state.imgBaseUrl = await PageSnapshot(elementHtml)
	nextTick(() => {
	   state.dialogSnapshot = true
	})	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号