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>

标题:如何手写一个切图放大器

作者:southernMD

发布于:

评论
昵称
邮箱
网站
评论
0 / 125
评论列表(0)
移至左侧
回到顶部
日间模式
开启音乐
隐藏面板
a