Files
aida_front/src/component/modules/liquefaction.vue
2024-10-03 16:17:15 +08:00

417 lines
12 KiB
Vue

<template>
<a-modal
class="liquefaction generalModel"
v-model:visible="liqufeaction"
:footer="null"
width="78%"
:maskClosable="false"
:centered="true"
:closable="false"
wrapClassName="#app"
:keyboard="false"
>
<div class="generalModel_btn">
<div class="generalModel_closeIcon" @click.stop="cancelDsign()">
<i class="fi fi-rr-cross-small"></i>
</div>
</div>
<div class="liquefaction_center">
<div class="liquefaction_canvas_box">
<canvas id="c"></canvas>
<div v-show="arrows.show" class="moveDom" :style="arrows.domStyle"></div>
<div v-show="arrows.show" class="jiantouDom" :style="arrows.jiantouStyle"> </div>
</div>
<div class="liquefaction_parameter">
<div class="liquefaction_parameter_item">
<div class="liquefaction_parameter_item_title">
<span>{{ $t('exportModel.Size') }}</span>
<input type="number" v-model="routes" @change="routesChange('routes')">
</div>
<div class="liquefaction_parameter_item_input">
<input type="range" v-model="routes" @change="routesChange('routes')">
</div>
</div>
<div class="liquefaction_parameter_item">
<div class="liquefaction_parameter_item_title">
<span>{{ $t('exportModel.density') }}</span>
<input type="number" v-model="density" @change="routesChange('density')">
</div>
<div class="liquefaction_parameter_item_input">
<input type="range" v-model="density" @change="routesChange('density')">
</div>
</div>
<div class="liquefaction_parameter_item">
<div class="generage_btn started_btn" @click="cancelDsign">取消</div>
<div class="generage_btn started_btn" @click="submit">确定</div>
</div>
</div>
</div>
</a-modal>
</template>
<script>
import { defineComponent, ref, reactive, watch, onMounted, nextTick, toRefs } from "vue";
import { useI18n } from "vue-i18n";
import { getMousePosition } from "@/tool/mdEvent";
export default defineComponent({
components: {
},
emits: ['submitLiquefaction'],
setup(props,{emit}) {
// let presentState = ref('paypal');
let liqufeaction = ref(false);
let liqufeactionData = reactive({
routes: 30,//移动框大小
density: 30,//移动大小
})
let arrows = ref({
show:false,
domStyle:{
left:0,
top:0,
width:0,
height:0,
},
jiantouStyle:{
height:'1px',
transform:`translateY(-100%) rotate(${90}deg)`,
}
})
let canvas
let canvasImgData = {
width:0,
height:0,
}
let downX
let downY
let init = async (data)=>{
liqufeaction.value = true
await new Promise((resolve, reject) => {
nextTick(()=>{
resolve()
})
})
let img = new Image()
img.setAttribute('crossOrigin', 'Anonymous')
img.onload = () => {
canvasImgData.width = img.width
canvasImgData.height = img.height
let canvasBox = document.querySelector('.liquefaction .liquefaction_canvas_box')
let width = canvasBox.offsetHeight / img.height * img.width
canvas = document.getElementById('c')
canvas.width = width
canvas.height = canvasBox.offsetHeight
let optfor = false
const context = canvas.getContext('2d');
context.drawImage(img, 1, 1, canvas.width, canvas.height);
let imgData = context.getImageData(0, 0, canvas.width, canvas.height)
let canvasDownX
let canvasDownY
let distance = 0
let angle
let mousedown = (event)=>{
let e = getMousePosition(event,false)
down(e)
}
let touchdown = (event)=>{
let e = getMousePosition(event,true)
down(e)
}
let down = (event)=> {
let canvasPosition = canvas.getBoundingClientRect()
let maxX = canvasPosition.width + canvasPosition.x
let minX = canvasPosition.x
let maxY = canvasPosition.height + canvasPosition.y
let minY = canvasPosition.y
if (event.clientX > minX && event.clientX < maxX && event.clientY > minY && event.clientY < maxY) {
downX = event.clientX
downY = event.clientY
canvasDownX = downX - minX
canvasDownY = downY - minY
}
optfor = true
arrows.value.jiantouStyle.height = distance + 'px'
arrows.value.jiantouStyle.left = event.clientX - canvasBox.getBoundingClientRect().left + 'px'
arrows.value.jiantouStyle.top = event.clientY - canvasBox.getBoundingClientRect().top + 'px'
arrows.value.domStyle.width = liqufeactionData.routes * 2 + 'px'
arrows.value.domStyle.height = liqufeactionData.routes * 2 + 'px'
arrows.value.domStyle.left = event.clientX - canvasBox.getBoundingClientRect().left - liqufeactionData.routes + 'px'
arrows.value.domStyle.top = event.clientY - canvasBox.getBoundingClientRect().top - liqufeactionData.routes + 'px'
arrows.value.show = true
let mouseup = ()=>{
if(!optfor)return
if(distance != 0){
let {upDownX, upDownY} = calculateTargetCoordinates(canvasDownX, canvasDownY,distance,angle)
liquify(imgData, canvasDownX, canvasDownY, upDownX, upDownY, liqufeactionData.routes, liqufeactionData.density)
}
distance = 0
arrows.value.show = false
context.putImageData(imgData, 0, 0);
optfor = false
canvasBox.removeEventListener('mousemove', mouseMove);
canvasBox.removeEventListener('touchmove', touchmove);
document.removeEventListener('mouseup', mouseup);
document.removeEventListener('touchend', mouseup);
}
canvasBox.addEventListener('mousemove', mouseMove);
canvasBox.addEventListener('touchmove', touchmove);
document.addEventListener('mouseup', mouseup);
document.addEventListener('touchend', mouseup);
}
let mouseMove = (event)=>{
let e = getMousePosition(event,false)
move(e)
}
let touchmove = (event)=>{
let e = getMousePosition(event,true)
move(e)
}
let move = function (moveEvent) {
let height = distanceFun(downX , downY , moveEvent.clientX , moveEvent.clientY)
if(optfor){
let height = distanceFun(downX , downY , moveEvent.clientX , moveEvent.clientY)
angle = angleFun(downX , downY , moveEvent.clientX , moveEvent.clientY)
if(height < liqufeactionData.routes){
distance = height
arrows.value.jiantouStyle.height = height + 'px'
}
arrows.value.jiantouStyle.transform = `translateY(-100%) rotate(${angle+90}deg)`
}
}
// canvas.addEventListener('mousedown', down);
canvasBox.addEventListener('mousedown', mousedown);
canvasBox.addEventListener('touchstart', touchdown);
}
img.src = data._element.src
}
let distanceSqr = (x1, y1, x2, y2) => sqr(x1 - x2) + sqr(y1 - y2);
let sqr = (x) => x * x;;
let eachCircleDot = (imageData, ox, oy, r, callback)=>{
var imgWidth = imageData.width,
imgHeight = imageData.height,
data = imageData.data,
left = ox - r,
right = ox + r,
top = oy - r,
bottom = oy + r,
dotRedOffset, dotGreenOffset, dotBlueOffset, alphaOffset;
for (var x = left; x < right; x++){
for (var y = top; y < bottom; y++){
if (distanceSqr(x, y, ox, oy) <= sqr(r)) {
dotRedOffset = y * imgWidth * 4 + x * 4;
dotGreenOffset = dotRedOffset + 1;
dotBlueOffset = dotGreenOffset + 1;
alphaOffset = dotBlueOffset + 1;
callback(
// 当前点的坐标
{ x: x, y: y },
// 点的RGBA四个分量对应字节的下标
{
r: dotRedOffset,
g: dotGreenOffset,
b: dotBlueOffset,
a: alphaOffset,
},
// 传进来的ImageData的data部分
data
);
}
}
}
}
let copyImageDataBuff = (imgData)=>{
var data = imgData.data,
imgDataBuff = [];
for (var i in data){
imgDataBuff[i] = data[i];
}
return imgDataBuff;
}
let moveDot = (imgData, dataBuff, x, y, srcX, srcY)=> {
var imgWidth = imgData.width,
imgHeight = imgData.height,
data = imgData.data;
// // 进行边界检查,确保不超出图像范围
x = Math.max(0, Math.min(Math.round(x), imgWidth - 1));
y = Math.max(0, Math.min(Math.round(y), imgHeight - 1));
srcX = Math.max(0, Math.min(Math.round(srcX), imgWidth - 1));
srcY = Math.max(0, Math.min(Math.round(srcY), imgHeight - 1));
srcX = Math.round(srcX);
srcY = Math.round(srcY);
var targetStartOffset = y * imgWidth * 4 + x * 4,
srcStartOffset = srcY * imgWidth * 4 + srcX * 4;
for (var i = 0; i < 4; i++)
data[targetStartOffset + i] = dataBuff[srcStartOffset + i];
}
let liquify = (imgData, cx, cy, mx, my, r, strenth) => {
var imgDataBuff = copyImageDataBuff(imgData);
eachCircleDot(imgData, cx, cy, r, function (posi) {
var tx = posi.x,
ty = posi.y;
var u = transFormula(cx, cy, mx, my, tx, ty, r, strenth);
moveDot(imgData, imgDataBuff, tx, ty, u.x, u.y);
function transFormula(cx, cy, mx, my, tx, ty, r, strenth) {
strenth = strenth || 100;
var relativity = sqr(r) - distanceSqr(tx, ty, cx, cy);
var distanceMovedSqr = distanceSqr(mx, my, cx, cy);
var rate = sqr(relativity / (relativity + distanceMovedSqr * (100 / strenth)));
var ux = tx - rate * (mx - cx),
uy = ty - rate * (my - cy);
return { x: ux, y: uy };
}
});
}
let distanceFun = (x1, y1, x2, y2) => {
return Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));
}
let angleFun = (x1, y1, x2, y2) => {
return Math.atan2(y2 - y1, x2 - x1) * (180 / Math.PI);
}
let calculateTargetCoordinates = (x1, y1, distance, angleDegrees) => {
let angleRad = angleDegrees * (Math.PI / 180);
let x2 = x1 + distance * Math.cos(angleRad);
let y2 = y1 + distance * Math.sin(angleRad);
return { upDownX: x2, upDownY: y2 };
}
let cancelDsign = ()=>{
liqufeaction.value = false
}
let routesChange = (str)=>{
liqufeactionData[str] = Math.round(liqufeactionData[str]/10)*10;
if (liqufeactionData[str] < 10) {
liqufeactionData[str] = 10; // 设置为最小值
} else if (liqufeactionData[str] > 100) {
liqufeactionData[str] = 100; // 设置为最大值
}
}
let submit = ()=>{
const newCanvas = document.createElement('canvas');
newCanvas.width = canvasImgData.width;
newCanvas.height = canvasImgData.height;
const newCtx = newCanvas.getContext('2d');
newCtx.drawImage(
canvas,
0, 0,
canvas.width,
canvas.height,
0, 0,
newCanvas.width,
newCanvas.height
);
emit('submitLiquefaction',newCanvas.toDataURL('image/png'))
cancelDsign()
}
return {
liqufeaction,
...toRefs(liqufeactionData),
arrows,
init,
cancelDsign,
routesChange,
submit,
};
},
data() {
return {
};
},
mounted() {},
methods: {
},
});
</script>
<style lang="less" scoped>
.liquefaction {
background: #f9fafb;
.liquefaction_center{
height: 100%;
display: flex;
align-items: center;
.liquefaction_canvas_box{
background: #fff;
height: 90%;
width: 80%;
text-align: center;
// overflow-x: auto;
position: relative;
overflow-y: hidden;
}
.liquefaction_parameter{
padding-top: 5rem;
height: 100%;
flex: 1;
.liquefaction_parameter_item{
display: flex;
flex-direction: column;
.liquefaction_parameter_item_title{
display: flex;
justify-content: space-between;
input{
width: 30%;
text-align: right;
}
}
.liquefaction_parameter_item_input{
width: 100%;
input{
width: 100%;
}
}
}
.liquefaction_parameter_item:last-child{
margin-top: 20px;
text-align: center;
div{
margin-bottom: 1rem;
}
}
}
.moveDom {
width: 20px;
height: 20px;
border-radius: 50%;
box-sizing: border-box;
border: 1px solid;
position: absolute;
pointer-events: none;
}
.jiantouDom {
position: absolute;
width: 2px;
height: 1px;
background-color: #000;
transform-origin: center bottom;
transform: translateY(-100%);
pointer-events: none;
}
.jiantouDom::before {
content: "";
display: block;
position: absolute;
width: 10px;
height: 10px;
left: 50%;
transform: translateX(-50%) rotate(45deg);
border-top: 2px solid #000;
border-left: 2px solid #000;
pointer-events: none;
}
}
}
</style>