597 lines
17 KiB
Vue
597 lines
17 KiB
Vue
<template>
|
|
<div>
|
|
<div v-if="news" class="font-box">
|
|
<span />
|
|
<span style="margin-right: 70px">热门预览</span>
|
|
<span style="margin-right: 6px">非热门预览</span>
|
|
</div>
|
|
<div class="cropper-content">
|
|
<!-- 剪裁框 -->
|
|
<div class="cropper">
|
|
<vueCropper
|
|
ref="cropper"
|
|
:img="option.img"
|
|
:output-size="option.size"
|
|
:output-type="option.outputType"
|
|
:info="true"
|
|
:full="option.full"
|
|
:can-move="option.canMove"
|
|
:can-move-box="option.canMoveBox"
|
|
:original="option.original"
|
|
:auto-crop="option.autoCrop"
|
|
:auto-crop-width="option.autoCropWidth"
|
|
:auto-crop-height="option.autoCropHeight"
|
|
:fixed-box="option.fixedBox"
|
|
:fixed="option.fixed"
|
|
:fixed-number="fixedNumber"
|
|
@realTime="realTime"
|
|
/>
|
|
</div>
|
|
<!-- 预览框 -->
|
|
<div class="show-preview" :style="{'width': '300px', 'height': '300px', 'overflow': 'hidden', 'margin': '0 25px', 'display':'flex', 'align-items' : 'center'}" @mousedown="previewContainerDown" @mousemove="previewContainerMove" @mouseup="previewContainerUp" @mouseleave="previewContainerLeave">
|
|
<div :style="previews.div" style="position: relative">
|
|
<div :ref="previewId" :style="previews.div" class="preview">
|
|
<img :src="previews.url" :style="previews.img">
|
|
<img v-if="water.url" :src="water.url" :style="water.img" class="water">
|
|
</div>
|
|
<div v-show="water.url" :style="previews.div" class="water-cropper">
|
|
<!-- @mousemove.self="cropMove" -->
|
|
<div class="crop-box" :style="water.img" @mousedown.self="cropDown">
|
|
<span class="crop-line line-w" />
|
|
<span class="crop-line line-a" />
|
|
<span class="crop-line line-s" />
|
|
<span class="crop-line line-d" />
|
|
<span class="crop-point point1" @mousedown.stop="pointDown($event, 'lt')" />
|
|
<span class="crop-point point2" @mousedown.stop="pointDown($event, 'rt')" />
|
|
|
|
<span class="crop-point point3" @mousedown.stop="pointDown($event, 'lb')" />
|
|
|
|
<span class="crop-point point4" @mousedown.stop="pointDown($event, 'rb')" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- 新闻预览框 -->
|
|
<div v-if="news" class="news-preview" :style="{'width': '200px', 'height': '300px', 'overflow': 'hidden', 'margin': '0', 'display':'flex', 'align-items' : 'center'}">
|
|
<!-- <div class="news-box"> -->
|
|
<div :ref="previewId" :style="previews.div" class="preview">
|
|
<img :src="previews.url" :style="previews.img" style="margin-left: -75px">
|
|
<img v-if="water.url" :src="water.url" :style="water.img" style="margin-left: -75px" class="water">
|
|
</div>
|
|
<!-- </div> -->
|
|
</div>
|
|
</div>
|
|
<div class="footer-btn">
|
|
<!-- 缩放旋转按钮 -->
|
|
<div class="scope-btn">
|
|
<el-button type="primary" icon="el-icon-zoom-in" @click="changeScale(1)" />
|
|
<el-button type="primary" icon="el-icon-zoom-out" @click="changeScale(-1)" />
|
|
<el-button type="primary" @click="rotateLeft">逆时针旋转</el-button>
|
|
<el-button type="primary" @click="rotateRight">顺时针旋转</el-button>
|
|
</div>
|
|
<!-- 确认上传按钮 -->
|
|
<div class="upload-btn">
|
|
<el-button type="primary" @click="uploadImg('blob')">上传</el-button>
|
|
<el-button type="primary" @click="selectWater">选择水印</el-button>
|
|
<el-button type="primary" @click="water.url = null">取消水印</el-button>
|
|
<!-- <input type="file" name="" id="" accept=""> -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
// import VueCropper from 'vue-cropper'
|
|
// import html2canvas from 'html2canvas'
|
|
// import { createElement } from 'dropzone'
|
|
export default {
|
|
props: ['imgFile', 'fixedNumber', 'news'],
|
|
data() {
|
|
name:'Cropper'
|
|
return {
|
|
previewId: 'waterProview' + Date.parse(new Date()),
|
|
previews: {}, // 预览数据
|
|
option: {
|
|
img: '', // 裁剪图片的地址 (默认:空)
|
|
size: 1, // 裁剪生成图片的质量 (默认:1)
|
|
full: true, // 是否输出原图比例的截图 选true生成的图片会非常大 (默认:false)
|
|
outputType: 'png', // 裁剪生成图片的格式 (默认:jpg)
|
|
canMove: true, // 上传图片是否可以移动 (默认:true)
|
|
original: false, // 上传图片按照原始比例渲染 (默认:false)
|
|
canMoveBox: true, // 截图框能否拖动 (默认:true)
|
|
autoCrop: true, // 是否默认生成截图框 (默认:false)
|
|
autoCropWidth: 400, // 默认生成截图框宽度 (默认:80%)
|
|
autoCropHeight: 400, // 默认生成截图框高度 (默认:80%)
|
|
fixedBox: false, // 固定截图框大小 不允许改变 (默认:false)
|
|
fixed: true, // 是否开启截图框宽高固定比例 (默认:true)
|
|
fixedNumber: [1.5, 1] // 截图框比例 (默认:[1:1])
|
|
},
|
|
downImg: '#',
|
|
|
|
// 水印
|
|
waterUrl: null,
|
|
water: {
|
|
url: null,
|
|
img: {
|
|
width: '100px',
|
|
height: '100px',
|
|
left: '0px',
|
|
top: '0px'
|
|
}
|
|
},
|
|
waterBox: {
|
|
left: 0,
|
|
top: 0,
|
|
width: 100,
|
|
height: 100,
|
|
scale: 1,
|
|
prop: 1
|
|
},
|
|
waterInfo: {
|
|
waterX: 0,
|
|
waterY: 0,
|
|
x: 0,
|
|
y: 0
|
|
},
|
|
waterMove: false,
|
|
|
|
pointInfo: {
|
|
pointX: 0,
|
|
pointY: 0,
|
|
x: 0,
|
|
y: 0,
|
|
width: 0,
|
|
height: 0
|
|
},
|
|
pointType: null,
|
|
pointIsMove: false,
|
|
|
|
successimg: null
|
|
}
|
|
},
|
|
watch: {
|
|
waterBox: {
|
|
handler(newValue, oldValue) {
|
|
// console.log(newValue)
|
|
this.water.img.left = newValue.left + 'px'
|
|
this.water.img.top = newValue.top + 'px'
|
|
this.water.img.width = newValue.width + 'px'
|
|
this.water.img.height = newValue.height + 'px'
|
|
},
|
|
deep: true
|
|
}
|
|
},
|
|
methods: {
|
|
changeScale(num) {
|
|
// 图片缩放
|
|
num = num || 1
|
|
this.$refs.cropper.changeScale(num)
|
|
},
|
|
rotateLeft() {
|
|
// 向左旋转
|
|
console.log(this.$refs.cropper)
|
|
this.$refs.cropper.rotateLeft()
|
|
},
|
|
rotateRight() {
|
|
// 向右旋转
|
|
console.log(this.$refs.cropper)
|
|
this.$refs.cropper.rotateRight()
|
|
},
|
|
Update() {
|
|
// this.file = this.imgFile
|
|
this.option.img = this.imgFile.url
|
|
},
|
|
realTime(data) {
|
|
// 实时预览
|
|
this.previews = data
|
|
},
|
|
uploadImg(type) {
|
|
// 将剪裁好的图片回传给父组件
|
|
event.preventDefault()
|
|
const that = this
|
|
|
|
this.$refs.cropper.getCropData(async data => {
|
|
// that.$emit('upload', data)
|
|
let cropInfo, waterInfo
|
|
if (!this.water.url) {
|
|
if (type === 'blob') {
|
|
this.$refs.cropper.getCropBlob(data => {
|
|
that.$emit('upload', data)
|
|
})
|
|
} else {
|
|
this.$refs.cropper.getCropData(data => {
|
|
that.$emit('upload', data)
|
|
})
|
|
}
|
|
}
|
|
await (() => {
|
|
const cropImg = new Image()
|
|
cropImg.src = data
|
|
cropImg.onload = () => {
|
|
cropInfo = {
|
|
img: cropImg,
|
|
width: cropImg.width,
|
|
height: cropImg.height,
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
const waterImg = new Image()
|
|
waterImg.crossOrigin = ''
|
|
waterImg.src = this.water.url
|
|
waterImg.onload = () => {
|
|
waterInfo = {
|
|
img: waterImg,
|
|
width: this.waterBox.width,
|
|
height: this.waterBox.height,
|
|
x: this.waterBox.left,
|
|
y: this.waterBox.top
|
|
}
|
|
// console.log(cropInfo)
|
|
// console.log(waterInfo)
|
|
|
|
const canvas = document.createElement('canvas')
|
|
canvas.width = cropInfo.width
|
|
canvas.height = cropInfo.height
|
|
const ctx = canvas.getContext('2d')
|
|
ctx.drawImage(cropInfo.img, cropInfo.x, cropInfo.y, cropInfo.width, cropInfo.height)
|
|
// console.log(this.previews.div)
|
|
const prop = cropInfo.width / parseFloat(this.previews.div.width)
|
|
ctx.drawImage(waterInfo.img, waterInfo.x * prop, waterInfo.y * prop, waterInfo.width * prop, waterInfo.height * prop)
|
|
// console.log(this.previews)
|
|
if (type === 'blob') {
|
|
canvas.toBlob((blob) => {
|
|
that.$emit('upload', blob)
|
|
})
|
|
} else {
|
|
that.$emit('upload', canvas.toDataURL('image/png'))
|
|
}
|
|
|
|
/*
|
|
// 创建 canvas 标签
|
|
const canvas = document.createElement('canvas')
|
|
// 获取要生成图片的 DOM 元素
|
|
const canvasDom = this.$refs[this.previewId]
|
|
// 获取指定的宽高
|
|
const width = parseInt(window.getComputedStyle(canvasDom).width)
|
|
const height = parseInt(window.getComputedStyle(canvasDom).height)
|
|
// 宽高扩大 2 倍 处理图片模糊
|
|
canvas.width = width * 2
|
|
canvas.height = height * 2
|
|
canvas.style.width = width + 'px'
|
|
canvas.style.height = height + 'px'
|
|
const context = canvas.getContext('2d')
|
|
context.scale(2, 2)
|
|
const options = {
|
|
backgroundColor: null,
|
|
canvas: canvas,
|
|
useCORS: true
|
|
}
|
|
console.log(123)
|
|
html2canvas(canvasDom, options).then(canvas => {
|
|
// console.log(canvas.toDataURL('image/png'))
|
|
// that.successimg = canvas.toDataURL('image/png')
|
|
// 生成图片地址
|
|
if (type === 'blob') {
|
|
console.log('blob')
|
|
canvas.toBlob((blob) => {
|
|
that.$emit('upload', blob)
|
|
})
|
|
} else {
|
|
that.$emit('upload', canvas.toDataURL('image/png'))
|
|
}
|
|
})
|
|
*/
|
|
}
|
|
}
|
|
})()
|
|
})
|
|
// return
|
|
// 原版
|
|
// if (type === 'blob') {
|
|
// this.$refs.cropper.getCropBlob(data => {
|
|
// that.$emit('upload', data)
|
|
// })
|
|
// } else {
|
|
// this.$refs.cropper.getCropData(data => {
|
|
// that.$emit('upload', data)
|
|
// })
|
|
// }
|
|
},
|
|
|
|
// 水印框的操作
|
|
selectWater() { // 选择水印
|
|
const input = document.createElement('input')
|
|
input.type = 'file'
|
|
input.accept = 'image/*'
|
|
input.addEventListener('change', (e) => {
|
|
const file = e.path[0].files[0]
|
|
const imgURL = window.URL.createObjectURL(file)
|
|
const image = new Image()
|
|
image.src = imgURL
|
|
image.onload = () => {
|
|
this.water.url = imgURL
|
|
const { width, height } = image
|
|
const prop = width / height
|
|
this.waterBox.prop = prop
|
|
this.waterBox.height = 100
|
|
this.waterBox.width = 100 * prop
|
|
}
|
|
})
|
|
setTimeout(() => {
|
|
input.click()
|
|
}, 100)
|
|
},
|
|
previewContainerDown(e) {
|
|
},
|
|
previewContainerMove(e) {
|
|
if (this.waterMove) {
|
|
this.cropMove(e)
|
|
} else if (this.pointIsMove) {
|
|
this.pointMove(e)
|
|
}
|
|
},
|
|
previewContainerUp() {
|
|
this.waterMove = false
|
|
this.pointIsMove = false
|
|
},
|
|
previewContainerLeave() {
|
|
this.waterMove = false
|
|
this.pointIsMove = false
|
|
},
|
|
cropDown(e) {
|
|
const { clientX, clientY } = e
|
|
this.waterInfo.x = this.waterBox.left
|
|
this.waterInfo.y = this.waterBox.top
|
|
this.waterInfo.waterX = clientX
|
|
this.waterInfo.waterY = clientY
|
|
this.waterMove = true
|
|
},
|
|
cropMove(e) {
|
|
if (this.waterMove) {
|
|
const { clientX, clientY } = e
|
|
this.waterBox.left = (clientX - this.waterInfo.waterX) + this.waterInfo.x
|
|
this.waterBox.top = (clientY - this.waterInfo.waterY) + this.waterInfo.y
|
|
}
|
|
},
|
|
|
|
// 水印框角的操作
|
|
pointDown(e, type) {
|
|
const { clientX, clientY } = e
|
|
|
|
this.pointInfo.x = this.waterBox.left
|
|
this.pointInfo.y = this.waterBox.top
|
|
|
|
this.pointInfo.width = this.waterBox.width
|
|
this.pointInfo.height = this.waterBox.height
|
|
|
|
this.pointInfo.pointX = clientX
|
|
this.pointInfo.pointY = clientY
|
|
|
|
this.pointType = type
|
|
this.pointIsMove = true
|
|
},
|
|
pointMove(e) {
|
|
if (this.pointIsMove) {
|
|
const { clientX, clientY } = e
|
|
const left = (clientX - this.pointInfo.pointX) + this.pointInfo.x
|
|
const top = (clientY - this.pointInfo.pointY) + this.pointInfo.y
|
|
if (this.pointType === 'lt') {
|
|
this.waterBox.left = left
|
|
this.waterBox.top = top
|
|
|
|
const height = this.pointInfo.height - (clientY - this.pointInfo.pointY)
|
|
this.waterBox.height = height
|
|
this.waterBox.width = height * this.waterBox.prop
|
|
// this.waterBox.width = this.pointInfo.width - (clientX - this.pointInfo.pointX)
|
|
// this.waterBox.height = this.pointInfo.height - (clientY - this.pointInfo.pointY)
|
|
} else if (this.pointType === 'rt') {
|
|
this.waterBox.top = top
|
|
|
|
const height = this.pointInfo.height - (clientY - this.pointInfo.pointY)
|
|
this.waterBox.height = height
|
|
this.waterBox.width = height * this.waterBox.prop
|
|
// this.waterBox.width = this.pointInfo.width + (clientX - this.pointInfo.pointX)
|
|
// this.waterBox.height = this.pointInfo.height - (clientY - this.pointInfo.pointY)
|
|
} else if (this.pointType === 'lb') {
|
|
this.waterBox.left = left
|
|
|
|
const height = this.pointInfo.height + (clientY - this.pointInfo.pointY)
|
|
this.waterBox.height = height
|
|
this.waterBox.width = height * this.waterBox.prop
|
|
// this.waterBox.width = this.pointInfo.width - (clientX - this.pointInfo.pointX)
|
|
// this.waterBox.height = this.pointInfo.height + (clientY - this.pointInfo.pointY)
|
|
} else if (this.pointType === 'rb') {
|
|
const height = this.pointInfo.height + (clientY - this.pointInfo.pointY)
|
|
this.waterBox.height = height
|
|
this.waterBox.width = height * this.waterBox.prop
|
|
// this.waterBox.width = this.pointInfo.width + (clientX - this.pointInfo.pointX)
|
|
// this.waterBox.height = this.pointInfo.height + (clientY - this.pointInfo.pointY)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// components: { VueCropper }
|
|
}
|
|
</script>
|
|
<style>
|
|
.cropper-content {
|
|
display: flex;
|
|
display: -webkit-flex;
|
|
justify-content: flex-end;
|
|
-webkit-justify-content: flex-end;
|
|
}
|
|
.cropper-content .cropper {
|
|
width: 350px;
|
|
height: 300px;
|
|
}
|
|
.cropper-content .show-preview {
|
|
flex: 1;
|
|
-webkit-flex: 1;
|
|
display: flex;
|
|
display: -webkit-flex;
|
|
justify-content: center;
|
|
-webkit-justify-content: center;
|
|
overflow: hidden;
|
|
border: 1px solid #cccccc;
|
|
background: #cccccc;
|
|
margin-left: 40px;
|
|
}
|
|
.preview {
|
|
overflow: hidden;
|
|
border: 1px solid #cccccc;
|
|
background: #cccccc;
|
|
}
|
|
.footer-btn {
|
|
margin-top: 30px;
|
|
display: flex;
|
|
display: -webkit-flex;
|
|
justify-content: flex-end;
|
|
-webkit-justify-content: flex-end;
|
|
}
|
|
.footer-btn .scope-btn {
|
|
width: 380px;
|
|
display: flex;
|
|
display: -webkit-flex;
|
|
justify-content: space-between;
|
|
-webkit-justify-content: space-between;
|
|
}
|
|
.footer-btn .upload-btn {
|
|
flex: 1;
|
|
-webkit-flex: 1;
|
|
display: flex;
|
|
display: -webkit-flex;
|
|
justify-content: center;
|
|
-webkit-justify-content: center;
|
|
}
|
|
.footer-btn .btn {
|
|
outline: none;
|
|
display: inline-block;
|
|
line-height: 1;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
-webkit-appearance: none;
|
|
text-align: center;
|
|
-webkit-box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
outline: 0;
|
|
margin: 0;
|
|
-webkit-transition: 0.1s;
|
|
transition: 0.1s;
|
|
font-weight: 500;
|
|
padding: 8px 15px;
|
|
font-size: 12px;
|
|
border-radius: 3px;
|
|
color: #fff;
|
|
background-color: #67c23a;
|
|
border-color: #67c23a;
|
|
}
|
|
|
|
/* 水印 */
|
|
.cropper-content .show-preview{
|
|
position: relative;
|
|
user-select: none;
|
|
}
|
|
.water-cropper{
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
/* display: flex;
|
|
align-items: center; */
|
|
}
|
|
.water{
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
}
|
|
|
|
.preview {
|
|
position: relative;
|
|
}
|
|
|
|
.crop-box{
|
|
/* position: relative; */
|
|
position: absolute;
|
|
width: 50px;
|
|
height: 50px;
|
|
cursor: move;
|
|
}
|
|
|
|
.crop-line, .crop-point{
|
|
background: #39f;
|
|
position: absolute;
|
|
user-select: none
|
|
}
|
|
.crop-line{
|
|
opacity: 0.5;
|
|
}
|
|
.line-w, .line-s{
|
|
height: 1px;
|
|
width: 100%;
|
|
}
|
|
.line-a, .line-d{
|
|
width: 1px;
|
|
height: 100%;
|
|
}
|
|
.line-w{
|
|
top: 0;
|
|
left: 0;
|
|
}
|
|
.line-s{
|
|
top: 100%;
|
|
left: 0;
|
|
}
|
|
.line-a{
|
|
top: 0;
|
|
left: 0;
|
|
}
|
|
.line-d{
|
|
top: 0;
|
|
left: 100%;
|
|
}
|
|
.crop-point{
|
|
width: 8px;
|
|
height: 8px;
|
|
opacity: 0.75;
|
|
background-color: #39f;
|
|
border-radius: 100%;
|
|
}
|
|
.water-cropper .point1{
|
|
top: -4px;
|
|
left: -4px;
|
|
cursor: nw-resize;
|
|
}
|
|
.water-cropper .point2{
|
|
top: -4px;
|
|
right: -4px;
|
|
cursor: ne-resize;
|
|
}
|
|
.water-cropper .point3{
|
|
left: -4px;
|
|
bottom: -4px;
|
|
cursor: sw-resize;
|
|
}
|
|
.water-cropper .point4{
|
|
right: -4px;
|
|
bottom: -4px;
|
|
cursor: se-resize;
|
|
}
|
|
/* #39f */
|
|
.news-preview{
|
|
border: 1px solid #cccccc;
|
|
background: #cccccc;
|
|
}
|
|
/* .show-preview.news{
|
|
display: flex;
|
|
align-items: center;
|
|
} */
|
|
/* .news .news-box{
|
|
overflow: hidden;
|
|
} */
|
|
|
|
.font-box{
|
|
display: flex;
|
|
justify-content: space-around;
|
|
padding-bottom: 10px;
|
|
}
|
|
</style>
|