southernMD 南山有壶酒
首页
文章
留言板
十年
关于
音乐
作词 : 偽物
分类
- 暂无内容
站点信息
标签云
目录
作词 : 偽物

如何手写一个切图放大器
2023/8/13 17:50:39 |
0 |
21
JS
以网易云音乐切图器为例
效果总览

如图所示
基础代码
我们需要三部分,提交按钮,预览框以及截图预览框
js
复制代码
<template>
<div class="contain">
<div class="title">切图器</div>
<div class="img-pick">
<div class="left">
<div class="bk">
<div class="img" ref="pickImgRef" :style="{ 'background-image': 'url(' + url + ')' }">
<div class="model" ref="modelRef"></div>
<div class="pick" ref="pickRef" @mousedown.stop.self="beginDarg">
<div class="point" ref="pointRef" @mousedown.stop.self="beginScale"></div>
</div>
</div>
</div>
</div>
<div class="right">
<div class="top">
<div class="bk">
<img class="img" id="adsas" ref="bigRef" :src="url">
</div>
<div class="txt"><span>大尺寸封面</span></div>
</div>
<div class="bottom">
<div class="bk">
<img class="img" ref="smallRef" :src="url">
</div>
<div class="txt"><span>小尺寸封面</span></div>
</div>
</div>
</div>
<div class="btns">
<el-button type="primary" @click="openPic" class="fa" style="color: var(--fontColor);">{{ cancelName }}</el-button>
<el-button @click="confirm" class="go">{{ confirmName }}</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import {ref } from 'vue'
const confirmName = '保存'
const cancelName = '重新选择'
const url = ref('')
</script>
less部分
less
复制代码
<style scoped lang="less">
@pick-bk-color: var(--pickBkColor, rgb(41, 43, 47));
@my-dialog-bk: var(--myDialogBk, rgb(54, 54, 54));
@font-color:var(--fontColor,rgba(255, 255, 255,.7));
@primary-color: var(--primaryColor,rgb(236,65,65));
@play-all-button-hover:var(--playAllButtonHover,rgb(204, 50, 50));
@span-color-hover:var(--spanColorHover,rgb(63, 63, 63));
@split-line-color:var(--splitLineColor,rgb(224,224,224));
.contain {
width: 30%;
height: 50vh;
background-color: @my-dialog-bk;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
color: @font-color;
>.title{
font-size: 20px;
height: 50px;
}
.img-pick {
display: flex;
justify-content: center;
align-items: center;
user-select: none;
.left {
.bk {
height: 235px;
width: 235px;
background-color: @pick-bk-color;
border-radius: .2em;
border: 1px solid black;
// transform: scale();
}
.img {
width: 100%;
height: 100%;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
position: relative;
z-index: 0;
.pick {
position: absolute;
box-sizing: border-box;
border: 1px dotted #00ff00;
z-index: 2;
background-origin: border-box;
background-repeat: no-repeat;
cursor: move;
.point {
background-origin: border-box;
height: 6px;
width: 5px;
position: absolute;
background-color: black;
z-index: 3;
right: -3px;
bottom: -4px;
cursor: nw-resize;
}
}
.model {
position: absolute;
background-color: rgba(0, 0, 0, .4);
box-sizing: border-box;
z-index: 1;
}
}
}
.right {
display: flex;
flex-direction: column;
margin-left: 30px;
.top {
height: 130px;
width: 110px;
display: flex;
flex-direction: column;
margin-bottom: 10px;
.bk {
height: 90px;
width: 90px;
overflow: hidden;
border-radius: .2em;
aspect-ratio: auto;
position: relative;
img {
position: absolute;
}
}
.txt {
height: 40px;
width: 100%;
display: flex;
align-items: center;
}
}
.bottom {
height: 100px;
width: 80px;
display: flex;
flex-direction: column;
.bk {
height: 60px;
width: 60px;
overflow: hidden;
border-radius: .2em;
aspect-ratio: auto;
position: relative;
img {
position: absolute;
}
}
.txt {
height: 40px;
width: 100%;
display: flex;
align-items: center;
}
}
}
}
>.btns{
margin-top: 20px;
.go {
background-color: @primary-color;
border: none;
color: #fff;
border-radius: 2em;
width: 100px;
&:hover {
background-color: @play-all-button-hover ;
}
}
.fa {
background-color: rgba(0, 0, 0, 0);
border: @split-line-color 1px solid;
width: 100px;
border-radius: 2em;
&:hover {
background-color: @span-color-hover !important;
}
}
}
}
</style>
这里重点解释一下这部分
html
复制代码
<div class="img" ref="pickImgRef" :style="{ 'background-image': 'url(' + url + ')' }">
<div class="model" ref="modelRef"></div>
<div class="pick" ref="pickRef" @mousedown.stop.self="beginDarg">
<div class="point" ref="pointRef" @mousedown.stop.self="beginScale"></div>
</div>
</div>
model是遮盖整个图片的灰色蒙版,pick的本质上是一个与选取图片相同url的背景图片区域;带有绿色裁剪框,用于高亮图片;point用于拉伸更改显示区域

如图所示
上传图片
点击重新选择时,上传一张图片,这边略过校验文件格式部分,我们需要将文件转换为base64然后赋值给url参数
js
复制代码
<script setup lang="ts">
import {ref } from 'vue'
const confirmName = '保存'
const cancelName = '重新选择'
const url = ref('')
const openPic = () => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.click()
fileInput.addEventListener('change',async()=>{
const file = fileInput.files![0];
url.value = await fileToBase64(file)
})
}
function fileToBase64(file:File):Promise<string>{
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function (event) {
resolve(event.target!.result as string)
};
reader.readAsDataURL(file);
})
}
</script>

如图所示
部署裁剪区域
裁剪框的计算方式是在图片区域左上定格显示,bgPosition参数包含图片相对于背景边框所相距的百分比,设置蒙版与高亮部分在范围内,去图片宽高较小边为蒙版的基础大小。在图片渲染到页面上会保持一定比例进行压缩,保证在固定区域内显示,而在提交图片时提交的是原图片和裁剪的坐标,坐标相对与实际坐标有一定比例缩放,这个缩放的大小是scale
js
复制代码
const bgWidth = ref(0)
const bgHeight = ref(0)
let initX = ref(0); //裁剪框坐标
let initY = ref(0);
const scale = ref(1)//缩放比例
const modelRef = ref<HTMLElement>()
const pickImgRef = ref<Element>()
const pickRef = ref<HTMLElement>()
const getBgImageSize = () => {
const pickImgEl = pickImgRef.value;
if (pickImgEl) {
const bgImage = new Image();
bgImage.src = URL.createObjectURL(imgObject!)
bgImage.onload = function () {
const bgImageWidth = bgImage.width;
const bgImageHeight = bgImage.height;
const bgPosition = getComputedStyle(pickImgEl).backgroundPosition.split(" ");
scale.value = Math.min(pickImgEl.clientWidth / bgImageWidth, pickImgEl.clientHeight / bgImageHeight);
initX.value = (pickImgEl.clientWidth - bgImageWidth * scale.value) * parseFloat(bgPosition[0]) / 100;
initY.value = (pickImgEl.clientHeight - bgImageHeight * scale.value) * parseFloat(bgPosition[1]) / 100;
bgWidth.value = bgImageWidth * scale.value; //选框的大小
bgHeight.value = bgImageHeight * scale.value;
const WHValue = Math.min(bgWidth.value, bgHeight.value)
pickRef.value!.style.left = initX.value + 'px'
pickRef.value!.style.top = initY.value + 'px'
pickRef.value!.style.width = WHValue + 'px'
pickRef.value!.style.height = WHValue + 'px'
pickRef.value!.style.backgroundImage = `url('${url.value}')`;
pickRef.value!.style.backgroundSize = `${bgWidth.value}px ${bgHeight.value}px`
modelRef.value!.style.width = bgWidth.value + 'px'
modelRef.value!.style.height = bgHeight.value + 'px'
modelRef.value!.style.left = initX.value + 'px'
modelRef.value!.style.top = initY.value + 'px'
// bigRef.value!.style.height = 90 + 'px'
// smallRef.value!.style.height = 60 + 'px'
}
}
}
在openPic函数内调用
js
复制代码
const openPic = () => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.click()
fileInput.addEventListener('change',async()=>{
imgObject = fileInput.files![0];
url.value = await fileToBase64(imgObject)
getBgImageSize()
})
}

如图所示
移动裁剪区域
已经移动距离 - 图片距离背景边框距离 + 现移动距离 + 裁剪框的宽度 与 背景宽度进行比较,修改裁剪框图片位置与背景框left top 值,同时限制不出背景框
js
复制代码
const pointRef = ref<HTMLElement>()
const MouseX = ref(0)
const MouseY = ref(0)
const beginDarg = (e: MouseEvent) => {
const pointEl = pointRef.value;
if (pointEl) {
MouseX.value = e.clientX
MouseY.value = e.clientY
window.addEventListener('mousemove', mouseDargMoving)
window.addEventListener('mouseup', mouseDargUp)
}
}
const mouseDargMoving = (e: MouseEvent) => {
const x = e.clientX - MouseX.value
const y = e.clientY - MouseY.value
const nowWHValue = +pickRef.value!.style.width.split('px')[0]
let flagX = false, flagY = false;
if (+pickRef.value!.style.left.split('px')[0] - initX.value + x + nowWHValue >= bgWidth.value) {
pickRef.value!.style.backgroundPositionX = -(bgWidth.value - nowWHValue) + 'px'
pickRef.value!.style.left = bgWidth.value - nowWHValue + 'px'
flagX = true
}
if (+pickRef.value!.style.top.split('px')[0] - initY.value + y + nowWHValue >= bgHeight.value) {
pickRef.value!.style.backgroundPositionY = -(bgHeight.value - nowWHValue) + 'px'
pickRef.value!.style.top = bgHeight.value - nowWHValue + initY.value + 'px'
flagY = true
}
if (+pickRef.value!.style.left.split('px')[0] - initX.value + x <= 0) {
pickRef.value!.style.backgroundPositionX = 0 + 'px'
pickRef.value!.style.left = 0 + 'px'
flagX = true
}
if (+pickRef.value!.style.top.split('px')[0] - initY.value + y <= 0) {
pickRef.value!.style.backgroundPositionY = 0 + 'px'
pickRef.value!.style.top = initY.value + 'px'
flagY = true
}
if (!flagX) {
pickRef.value!.style.backgroundPositionX = -(+pickRef.value!.style.left.split('px')[0] + x) + 'px'
pickRef.value!.style.left = +pickRef.value!.style.left.split('px')[0] + x + 'px'
}
if (!flagY) {
pickRef.value!.style.backgroundPositionY = -(+pickRef.value!.style.top.split('px')[0] + y - initY.value) + 'px'
pickRef.value!.style.top = +pickRef.value!.style.top.split('px')[0] + y + 'px'
}
MouseX.value = e.clientX
MouseY.value = e.clientY
}
const mouseDargUp = () => {
window.removeEventListener('mousemove', mouseDargMoving)
window.removeEventListener('mouseup', mouseDargUp)
}

如图所示
缩放裁剪区域
缩放区域就比较简单了,缩放上限设置为最小20px,最大不超过所选图片短边,缩放的大小以鼠标移动距离多的为基准。在缩放的同时,由于裁剪区域大小的变化,也需要调用mouseDargMoving来重新计算图片的位置。
js
复制代码
const beginScale = (e: MouseEvent) => {
const pointEl = pointRef.value;
if (pointEl) {
MouseX.value = e.clientX
MouseY.value = e.clientY
window.addEventListener('mousemove', mouseMoving)
window.addEventListener('mousemove', mouseDargMoving)
window.addEventListener('mouseup', mouseUp)
}
}
const mouseMoving = (e: MouseEvent) => {
const WHValue = Math.min(bgWidth.value, bgHeight.value)
let MouseVal = Math.max(Math.abs(e.clientX - MouseX.value), Math.abs(e.clientY - MouseY.value))
if (e.clientX - MouseX.value < 0 || e.clientY - MouseY.value < 0) MouseVal = -MouseVal
MouseX.value = e.clientX
MouseY.value = e.clientY
if (+pickRef.value!.style.width.split('px')[0] + MouseVal < 20 && MouseVal < 0) {
pickRef.value!.style.width = 20 + 'px'
pickRef.value!.style.height = 20 + 'px'
} else if (+pickRef.value!.style.width.split('px')[0] + MouseVal > WHValue && MouseVal > 0) {
pickRef.value!.style.width = WHValue + 'px'
pickRef.value!.style.height = WHValue + 'px'
} else {
pickRef.value!.style.width = pickRef.value!.offsetWidth + MouseVal + 'px'
pickRef.value!.style.height = pickRef.value!.offsetHeight + MouseVal + 'px'
}
}
const mouseUp = () => {
window.removeEventListener('mousemove', mouseMoving)
window.removeEventListener('mousemove', mouseDargMoving)
window.removeEventListener('mouseup', mouseUp)
}

如图所示
同步预览框
一个预览框大小为6060,一个为9090可,根据需要进行修改
js
复制代码
const bigRef = ref<HTMLElement>()
const smallRef = ref<HTMLElement>()
const getBgImageSize = () => {
const pickImgEl = pickImgRef.value;
if (pickImgEl) {
const bgImage = new Image();
bgImage.src = URL.createObjectURL(imgObject!)
bgImage.onload = function () {
.....
bigRef.value!.style.height = 90 + 'px'
smallRef.value!.style.height = 60 + 'px'
}
}
}
const mouseDargMoving = (e: MouseEvent) => {
.....
if (+pickRef.value!.style.left.split('px')[0] - initX.value + x + nowWHValue >= bgWidth.value) {
.....
bigRef.value!.style.left = -(bgWidth.value - nowWHValue) * (90 / nowWHValue) + 'px'
smallRef.value!.style.left = -(bgWidth.value - nowWHValue) * (60 / nowWHValue) + 'px'
}
if (+pickRef.value!.style.top.split('px')[0] - initY.value + y + nowWHValue >= bgHeight.value) {
.....
bigRef.value!.style.top = -(bgHeight.value - nowWHValue) * (90 / nowWHValue) + 'px'
smallRef.value!.style.top = -(bgHeight.value - nowWHValue) * (60 / nowWHValue) + 'px'
}
if (+pickRef.value!.style.left.split('px')[0] - initX.value + x <= 0) {
.....
bigRef.value!.style.left = 0 + 'px'
smallRef.value!.style.left = 0 + 'px'
}
if (+pickRef.value!.style.top.split('px')[0] - initY.value + y <= 0) {
.....
bigRef.value!.style.top = 0 + 'px'
smallRef.value!.style.top = 0 + 'px'
}
if (!flagX) {
.....
bigRef.value!.style.left = -(+pickRef.value!.style.left.split('px')[0] + x) * (90 / nowWHValue) + 'px'
smallRef.value!.style.left = -(+pickRef.value!.style.left.split('px')[0] + x) * (60 / nowWHValue) + 'px'
}
if (!flagY) {
.....
bigRef.value!.style.top = -(+pickRef.value!.style.top.split('px')[0] + y - initY.value) * (90 / nowWHValue) + 'px'
smallRef.value!.style.top = -(+pickRef.value!.style.top.split('px')[0] + y - initY.value) * (60 / nowWHValue) + 'px'
}
.....
}
const mouseMoving = (e: MouseEvent) => {
.....
if (+pickRef.value!.style.width.split('px')[0] + MouseVal < 20 && MouseVal < 0) {
.....
bigRef.value!.style.height = WHValue / 20 * 90 + 'px'
smallRef.value!.style.height = WHValue / 20 * 60 + 'px'
} else if (+pickRef.value!.style.width.split('px')[0] + MouseVal > WHValue && MouseVal > 0) {
.....
bigRef.value!.style.height = 90 + 'px'
smallRef.value!.style.height = 60 + 'px'
} else {
......
bigRef.value!.style.height = WHValue / (pickRef.value!.offsetWidth + MouseVal) * 90 + 'px'
smallRef.value!.style.height = WHValue / (pickRef.value!.offsetHeight + MouseVal) * 60 + 'px'
}
}

如图所示
提交
提交原图片对象,裁剪宽度,X偏移量,Y偏移量到服务器,由服务器完成图片裁剪保存。
js
复制代码
const confirm = async () => {
let imgX = +pickRef.value!.style.left.split('px')[0] - initX.value
let imgY = +pickRef.value!.style.top.split('px')[0] - initY.value
if (imgX < 0) imgX = 0
if (imgY < 0) imgY = 0
const formData = new FormData()
formData.append('imgFile', imgObject!)
const nowWHValue = Math.ceil(+pickRef.value!.style.width.split('px')[0] / scale.value)
const X = Math.floor(imgX / scale.value)
const Y = Math.floor(imgY / scale.value)
//请求略
}
给出nodejs的例子
js
复制代码
const upload = multer();
router.post('/cover/update',upload.single('imgFile'),async(req:any,res:Response)=>{
const imageFile = req.file;
let {imgSize,imgX,imgY} = req.query
try {
// 使用 jimp 进行图像处理
const image = await jimp.read(imageFile.buffer);
const imageWidth = image.bitmap.width;
const imageHeight = image.bitmap.height;
if(imgSize > Math.min(imageHeight,imageWidth))imgSize = Math.min(imageHeight,imageWidth)
// 将处理后的图像转换为 Base64 编码
image.crop(+imgX, +imgY, +imgSize, +imgSize);
const imageBuffer = await image.getBufferAsync(jimp.MIME_JPEG); // 获取图像的缓冲区数据
const base64Image = imageBuffer.toString('base64');
res.json({code:200,data:{code:200}})
} catch (error) {
console.error('图片处理失败:', error);
res.status(500).json({ code:500,error: 'Image processing failed' ,msg:'error'});
}
})
如果是通过浏览器下载
js
复制代码
const confirm = async () => {
let imgX = +pickRef.value!.style.left.split('px')[0] - initX.value
let imgY = +pickRef.value!.style.top.split('px')[0] - initY.value
if (imgX < 0) imgX = 0
if (imgY < 0) imgY = 0
const formData = new FormData()
formData.append('imgFile', imgObject!)
const nowWHValue = Math.ceil(+pickRef.value!.style.width.split('px')[0] / scale.value)
const X = Math.floor(imgX / scale.value)
const Y = Math.floor(imgY / scale.value)
if(true){
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d');
const img = new Image();
img.src = URL.createObjectURL(imgObject!)
img.onload = function () {
canvas.width = nowWHValue;
canvas.height = nowWHValue;
context!.drawImage(img, X, Y, nowWHValue, nowWHValue, 0, 0, nowWHValue, nowWHValue)
const downloadUrl = canvas.toDataURL('image/jpeg')
const downloadLink = document.createElement('a');
downloadLink.href = downloadUrl;
downloadLink.download = Date.now() + '.jpg';
downloadLink.textContent = 'Download Image';
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
URL.revokeObjectURL(downloadUrl);
};
}
}
打印base64
js
复制代码
if(true){
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d');
const img = new Image();
img.src = URL.createObjectURL(imgObject!)
img.onload = function () {
canvas.width = nowWHValue;
canvas.height = nowWHValue;
context!.drawImage(img, X, Y, nowWHValue, nowWHValue, 0, 0, nowWHValue, nowWHValue)
canvas.toBlob(function(blob) {
const reader = new FileReader();
reader.onload = function(event) {
const base64String = event.target!.result;
console.log('Base64:', base64String);
};
reader.readAsDataURL(blob!);
}, 'image/jpeg');
};
}
完整代码
s
复制代码
<template>
<div class="contain">
<div class="title">切图器</div>
<div class="img-pick">
<div class="left">
<div class="bk">
<div class="img" ref="pickImgRef" :style="{ 'background-image': 'url(' + url + ')' }">
<div class="model" ref="modelRef"></div>
<div class="pick" ref="pickRef" @mousedown.stop.self="beginDarg">
<div class="point" ref="pointRef" @mousedown.stop.self="beginScale"></div>
</div>
</div>
</div>
</div>
<div class="right">
<div class="top">
<div class="bk">
<img class="img" id="adsas" ref="bigRef" :src="url">
</div>
<div class="txt"><span>大尺寸封面</span></div>
</div>
<div class="bottom">
<div class="bk">
<img class="img" ref="smallRef" :src="url">
</div>
<div class="txt"><span>小尺寸封面</span></div>
</div>
</div>
</div>
<div class="btns">
<el-button type="primary" @click="openPic" class="fa" style="color: var(--fontColor);">{{ cancelName }}</el-button>
<el-button @click="confirm" class="go">{{ confirmName }}</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import {ref } from 'vue'
const confirmName = '保存'
const cancelName = '重新选择'
const url = ref('')
let imgObject:File | null = null
const openPic = () => {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.click()
fileInput.addEventListener('change',async()=>{
imgObject = fileInput.files![0];
url.value = await fileToBase64(imgObject)
getBgImageSize()
})
}
function fileToBase64(file:File):Promise<string>{
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function (event) {
resolve(event.target!.result as string)
};
reader.readAsDataURL(file);
})
}
const bgWidth = ref(0)
const bgHeight = ref(0)
let initX = ref(0); //裁剪框坐标
let initY = ref(0);
const scale = ref(1)
const modelRef = ref<HTMLElement>()
const pickImgRef = ref<Element>()
const pickRef = ref<HTMLElement>()
const getBgImageSize = () => {
const pickImgEl = pickImgRef.value;
if (pickImgEl) {
const bgImage = new Image();
bgImage.src = URL.createObjectURL(imgObject!)
bgImage.onload = function () {
const bgImageWidth = bgImage.width;
const bgImageHeight = bgImage.height;
const bgPosition = getComputedStyle(pickImgEl).backgroundPosition.split(" ");
scale.value = Math.min(pickImgEl.clientWidth / bgImageWidth, pickImgEl.clientHeight / bgImageHeight);
initX.value = (pickImgEl.clientWidth - bgImageWidth * scale.value) * parseFloat(bgPosition[0]) / 100;
initY.value = (pickImgEl.clientHeight - bgImageHeight * scale.value) * parseFloat(bgPosition[1]) / 100;
bgWidth.value = bgImageWidth * scale.value; //选框的大小
bgHeight.value = bgImageHeight * scale.value;
console.log(scale.value);
// console.log(x,y,bgImageWidth * scale,bgImageHeight * scale);
const WHValue = Math.min(bgWidth.value, bgHeight.value)
pickRef.value!.style.left = initX.value + 'px'
pickRef.value!.style.top = initY.value + 'px'
pickRef.value!.style.width = WHValue + 'px'
pickRef.value!.style.height = WHValue + 'px'
pickRef.value!.style.backgroundImage = `url('${url.value}')`;
pickRef.value!.style.backgroundSize = `${bgWidth.value}px ${bgHeight.value}px`
pickRef.value!.style.backgroundPosition = '0px 0px'
modelRef.value!.style.width = bgWidth.value + 'px'
modelRef.value!.style.height = bgHeight.value + 'px'
modelRef.value!.style.left = initX.value + 'px'
modelRef.value!.style.top = initY.value + 'px'
bigRef.value!.style.height = 90 + 'px'
smallRef.value!.style.height = 60 + 'px'
}
}
}
const bigRef = ref<HTMLElement>()
const smallRef = ref<HTMLElement>()
const pointRef = ref<HTMLElement>()
const MouseX = ref(0)
const MouseY = ref(0)
const beginDarg = (e: MouseEvent) => {
const pointEl = pointRef.value;
if (pointEl) {
MouseX.value = e.clientX
MouseY.value = e.clientY
window.addEventListener('mousemove', mouseDargMoving)
window.addEventListener('mouseup', mouseDargUp)
}
}
const mouseDargMoving = (e: MouseEvent) => {
const x = e.clientX - MouseX.value
const y = e.clientY - MouseY.value
const nowWHValue = +pickRef.value!.style.width.split('px')[0]
let flagX = false, flagY = false;
if (+pickRef.value!.style.left.split('px')[0] - initX.value + x + nowWHValue >= bgWidth.value) {
pickRef.value!.style.backgroundPositionX = -(bgWidth.value - nowWHValue) + 'px'
bigRef.value!.style.left = -(bgWidth.value - nowWHValue) * (90 / nowWHValue) + 'px'
smallRef.value!.style.left = -(bgWidth.value - nowWHValue) * (60 / nowWHValue) + 'px'
pickRef.value!.style.left = bgWidth.value - nowWHValue + 'px'
flagX = true
}
if (+pickRef.value!.style.top.split('px')[0] - initY.value + y + nowWHValue >= bgHeight.value) {
pickRef.value!.style.backgroundPositionY = -(bgHeight.value - nowWHValue) + 'px'
bigRef.value!.style.top = -(bgHeight.value - nowWHValue) * (90 / nowWHValue) + 'px'
smallRef.value!.style.top = -(bgHeight.value - nowWHValue) * (60 / nowWHValue) + 'px'
pickRef.value!.style.top = bgHeight.value - nowWHValue + initY.value + 'px'
flagY = true
}
if (+pickRef.value!.style.left.split('px')[0] - initX.value + x <= 0) {
pickRef.value!.style.backgroundPositionX = 0 + 'px'
bigRef.value!.style.left = 0 + 'px'
smallRef.value!.style.left = 0 + 'px'
pickRef.value!.style.left = 0 + 'px'
flagX = true
}
if (+pickRef.value!.style.top.split('px')[0] - initY.value + y <= 0) {
pickRef.value!.style.backgroundPositionY = 0 + 'px'
bigRef.value!.style.top = 0 + 'px'
smallRef.value!.style.top = 0 + 'px'
pickRef.value!.style.top = initY.value + 'px'
flagY = true
}
if (!flagX) {
pickRef.value!.style.backgroundPositionX = -(+pickRef.value!.style.left.split('px')[0] + x) + 'px'
bigRef.value!.style.left = -(+pickRef.value!.style.left.split('px')[0] + x) * (90 / nowWHValue) + 'px'
smallRef.value!.style.left = -(+pickRef.value!.style.left.split('px')[0] + x) * (60 / nowWHValue) + 'px'
pickRef.value!.style.left = +pickRef.value!.style.left.split('px')[0] + x + 'px'
}
if (!flagY) {
pickRef.value!.style.backgroundPositionY = -(+pickRef.value!.style.top.split('px')[0] + y - initY.value) + 'px'
bigRef.value!.style.top = -(+pickRef.value!.style.top.split('px')[0] + y - initY.value) * (90 / nowWHValue) + 'px'
smallRef.value!.style.top = -(+pickRef.value!.style.top.split('px')[0] + y - initY.value) * (60 / nowWHValue) + 'px'
pickRef.value!.style.top = +pickRef.value!.style.top.split('px')[0] + y + 'px'
}
MouseX.value = e.clientX
MouseY.value = e.clientY
}
const mouseDargUp = () => {
window.removeEventListener('mousemove', mouseDargMoving)
window.removeEventListener('mouseup', mouseDargUp)
}
const beginScale = (e: MouseEvent) => {
const pointEl = pointRef.value;
if (pointEl) {
MouseX.value = e.clientX
MouseY.value = e.clientY
window.addEventListener('mousemove', mouseMoving)
window.addEventListener('mousemove', mouseDargMoving)
window.addEventListener('mouseup', mouseUp)
}
}
const mouseMoving = (e: MouseEvent) => {
const WHValue = Math.min(bgWidth.value, bgHeight.value)
let MouseVal = Math.max(Math.abs(e.clientX - MouseX.value), Math.abs(e.clientY - MouseY.value))
if (e.clientX - MouseX.value < 0 || e.clientY - MouseY.value < 0) MouseVal = -MouseVal
MouseX.value = e.clientX
MouseY.value = e.clientY
if (+pickRef.value!.style.width.split('px')[0] + MouseVal < 20 && MouseVal < 0) {
pickRef.value!.style.width = 20 + 'px'
pickRef.value!.style.height = 20 + 'px'
bigRef.value!.style.height = WHValue / 20 * 90 + 'px'
smallRef.value!.style.height = WHValue / 20 * 60 + 'px'
} else if (+pickRef.value!.style.width.split('px')[0] + MouseVal > WHValue && MouseVal > 0) {
pickRef.value!.style.width = WHValue + 'px'
pickRef.value!.style.height = WHValue + 'px'
bigRef.value!.style.height = 90 + 'px'
smallRef.value!.style.height = 60 + 'px'
} else {
pickRef.value!.style.width = pickRef.value!.offsetWidth + MouseVal + 'px'
pickRef.value!.style.height = pickRef.value!.offsetHeight + MouseVal + 'px'
bigRef.value!.style.height = WHValue / (pickRef.value!.offsetWidth + MouseVal) * 90 + 'px'
smallRef.value!.style.height = WHValue / (pickRef.value!.offsetHeight + MouseVal) * 60 + 'px'
}
}
const mouseUp = () => {
window.removeEventListener('mousemove', mouseMoving)
window.removeEventListener('mousemove', mouseDargMoving)
window.removeEventListener('mouseup', mouseUp)
}
const confirm = async () => {
let imgX = +pickRef.value!.style.left.split('px')[0] - initX.value
let imgY = +pickRef.value!.style.top.split('px')[0] - initY.value
if (imgX < 0) imgX = 0
if (imgY < 0) imgY = 0
const formData = new FormData()
formData.append('imgFile', imgObject!)
const nowWHValue = Math.ceil(+pickRef.value!.style.width.split('px')[0] / scale.value)
const X = Math.floor(imgX / scale.value)
const Y = Math.floor(imgY / scale.value)
if(true){
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d');
const img = new Image();
img.src = URL.createObjectURL(imgObject!)
img.onload = function () {
canvas.width = nowWHValue;
canvas.height = nowWHValue;
context!.drawImage(img, X, Y, nowWHValue, nowWHValue, 0, 0, nowWHValue, nowWHValue)
const downloadUrl = canvas.toDataURL('image/jpeg')
const downloadLink = document.createElement('a');
downloadLink.href = downloadUrl;
downloadLink.download = Date.now() + '.jpg';
downloadLink.textContent = 'Download Image';
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
URL.revokeObjectURL(downloadUrl);
};
}
}
</script>
<style scoped lang="less">
@pick-bk-color: var(--pickBkColor, rgb(41, 43, 47));
@my-dialog-bk: var(--myDialogBk, rgb(54, 54, 54));
@font-color:var(--fontColor,rgba(255, 255, 255,.7));
@primary-color: var(--primaryColor,rgb(236,65,65));
@play-all-button-hover:var(--playAllButtonHover,rgb(204, 50, 50));
@span-color-hover:var(--spanColorHover,rgb(63, 63, 63));
@split-line-color:var(--splitLineColor,rgb(224,224,224));
.contain {
width: 30%;
height: 50vh;
background-color: @my-dialog-bk;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
color: @font-color;
>.title{
font-size: 20px;
height: 50px;
}
.img-pick {
display: flex;
justify-content: center;
align-items: center;
user-select: none;
.left {
.bk {
height: 235px;
width: 235px;
background-color: @pick-bk-color;
border-radius: .2em;
border: 1px solid black;
// transform: scale();
}
.img {
width: 100%;
height: 100%;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
position: relative;
z-index: 0;
.pick {
position: absolute;
box-sizing: border-box;
border: 1px dotted #00ff00;
z-index: 2;
background-origin: border-box;
background-repeat: no-repeat;
cursor: move;
.point {
background-origin: border-box;
height: 6px;
width: 5px;
position: absolute;
background-color: black;
z-index: 3;
right: -3px;
bottom: -4px;
cursor: nw-resize;
}
}
.model {
position: absolute;
background-color: rgba(0, 0, 0, .4);
box-sizing: border-box;
z-index: 1;
}
}
}
.right {
display: flex;
flex-direction: column;
margin-left: 30px;
.top {
height: 130px;
width: 110px;
display: flex;
flex-direction: column;
margin-bottom: 10px;
.bk {
height: 90px;
width: 90px;
overflow: hidden;
border-radius: .2em;
aspect-ratio: auto;
position: relative;
img {
position: absolute;
}
}
.txt {
height: 40px;
width: 100%;
display: flex;
align-items: center;
}
}
.bottom {
height: 100px;
width: 80px;
display: flex;
flex-direction: column;
.bk {
height: 60px;
width: 60px;
overflow: hidden;
border-radius: .2em;
aspect-ratio: auto;
position: relative;
img {
position: absolute;
}
}
.txt {
height: 40px;
width: 100%;
display: flex;
align-items: center;
}
}
}
}
>.btns{
margin-top: 20px;
.go {
background-color: @primary-color;
border: none;
color: #fff;
border-radius: 2em;
width: 100px;
&:hover {
background-color: @play-all-button-hover ;
}
}
.fa {
background-color: rgba(0, 0, 0, 0);
border: @split-line-color 1px solid;
width: 100px;
border-radius: 2em;
&:hover {
background-color: @span-color-hover !important;
}
}
}
}
</style>
评论
评论列表(0)
移至左侧
回到顶部
日间模式
开启音乐
隐藏面板
a