334 lines
8.6 KiB
Plaintext
334 lines
8.6 KiB
Plaintext
|
|
export type xSIZE = {
|
|
width : number,
|
|
height : number,
|
|
top : number,
|
|
left : number
|
|
}
|
|
export type xImageInfo = {
|
|
width : number,
|
|
height : number,
|
|
top : number,
|
|
left : number,
|
|
src : string,
|
|
}
|
|
export type xSIZEiMage = {
|
|
width : number,
|
|
height : number,
|
|
top : number,
|
|
left : number,
|
|
scale : number
|
|
}
|
|
export type xPOINT = {
|
|
x : number,
|
|
y : number
|
|
}
|
|
export type xCalculatePinchZoomOpts = {
|
|
initialPoints : xPOINT[],
|
|
currentPoints : xPOINT[],
|
|
originWidth : number, // 原始图片宽度
|
|
originHeight : number, // 原始图片高度
|
|
cropWidth : number, // 裁剪框宽度
|
|
cropHeight : number,
|
|
initialScale : number,
|
|
width : number,
|
|
height : number,
|
|
top : number,
|
|
left : number,
|
|
scale : number
|
|
}
|
|
export function alignImageToFrame(imageParams : xSIZEiMage, frameParams : xSIZE) : xSIZE {
|
|
const {
|
|
width: imgWidth,
|
|
height: imgHeight,
|
|
top: imgTop,
|
|
left: imgLeft
|
|
} = imageParams;
|
|
|
|
const {
|
|
width: frameWidth,
|
|
height: frameHeight,
|
|
top: frameTop,
|
|
left: frameLeft
|
|
} = frameParams;
|
|
|
|
// 图片是否完全覆盖框(修改为严格大于)
|
|
const isWidthCovered = imgWidth > frameWidth;
|
|
const isHeightCovered = imgHeight > frameHeight;
|
|
|
|
// 当图片宽高都大于框时,保持自由拖动
|
|
// 同时图片确保覆盖住裁剪框.
|
|
if (isWidthCovered
|
|
&& isHeightCovered
|
|
&& imgLeft < frameLeft
|
|
&& imgTop < frameTop
|
|
&& (imgLeft + imgWidth) > (frameLeft + frameWidth)
|
|
&& (imgTop + imgHeight) > (frameTop + frameHeight)
|
|
) {
|
|
return {
|
|
width: imgWidth,
|
|
height: imgHeight,
|
|
top: imgTop,
|
|
left: imgLeft
|
|
};
|
|
}
|
|
|
|
let newTop = imgTop;
|
|
let newLeft = imgLeft;
|
|
|
|
// 图片的左侧是不是小于框的右侧
|
|
let isMinLeft = imgLeft < frameLeft
|
|
let isMaxRight = imgLeft + imgWidth > frameLeft + frameWidth
|
|
let isMinTop = imgTop < frameTop
|
|
let isMaxTop = imgTop + imgHeight > frameTop + frameHeight
|
|
// 水平对齐逻辑(反向对齐),如果越过左边了要左对齐,
|
|
if (imgLeft < frameLeft && !isMaxRight) {
|
|
// 图片左侧越界,靠右对齐
|
|
newLeft = frameLeft + frameWidth - imgWidth;
|
|
} else if (imgLeft + imgWidth > frameLeft + frameWidth && !isMinLeft) {
|
|
// 图片右侧越界,靠左对齐
|
|
newLeft = frameLeft;
|
|
}
|
|
|
|
// 垂直对齐逻辑(反向对齐)
|
|
if (imgTop < frameTop && !isMaxTop) {
|
|
// 图片顶部越界,靠底对齐
|
|
newTop = frameTop + frameHeight - imgHeight;
|
|
|
|
} else if (imgTop + imgHeight > frameTop + frameHeight && !isMinTop) {
|
|
// 图片底部越界,靠顶对齐
|
|
newTop = frameTop;
|
|
}
|
|
|
|
return {
|
|
width: imgWidth,
|
|
height: imgHeight,
|
|
top: newTop,
|
|
left: newLeft
|
|
};
|
|
}
|
|
function scaleImageToFitFrame(imageParams : xImageInfo, frameParams : xSIZE) : xSIZEiMage {
|
|
// 解构图片和裁剪框的参数
|
|
const {
|
|
width: imgWidth,
|
|
height: imgHeight,
|
|
top: imgTop = 0,
|
|
left: imgLeft = 0
|
|
} = imageParams;
|
|
|
|
const {
|
|
width: frameWidth,
|
|
height: frameHeight,
|
|
top: frameTop = 0,
|
|
left: frameLeft = 0
|
|
} = frameParams;
|
|
|
|
// 计算宽高比
|
|
const imgRatio = imgWidth / imgHeight;
|
|
const frameRatio = frameWidth / frameHeight;
|
|
|
|
let newWidth = 0
|
|
let newHeight = 0
|
|
let scale = 1
|
|
|
|
// 确保填充整个裁剪框
|
|
if (imgRatio > frameRatio) {
|
|
// 以高度为基准缩放
|
|
newHeight = frameHeight;
|
|
newWidth = newHeight * imgRatio;
|
|
scale = newHeight / imgHeight;
|
|
|
|
// 如果宽度不够,再次调整
|
|
if (newWidth < frameWidth) {
|
|
newWidth = frameWidth;
|
|
newHeight = newWidth / imgRatio;
|
|
scale = newWidth / imgWidth;
|
|
}
|
|
} else {
|
|
// 以宽度为基准缩放
|
|
newWidth = frameWidth;
|
|
newHeight = newWidth / imgRatio;
|
|
scale = newWidth / imgWidth;
|
|
|
|
// 如果高度不够,再次调整
|
|
if (newHeight < frameHeight) {
|
|
newHeight = frameHeight;
|
|
newWidth = newHeight * imgRatio;
|
|
scale = newHeight / imgHeight;
|
|
}
|
|
}
|
|
|
|
// 计算最终定位(考虑裁剪框和图片的初始偏移)
|
|
const top = frameTop + (frameHeight - newHeight) / 2 - imgTop * scale;
|
|
const left = frameLeft + (frameWidth - newWidth) / 2 - imgLeft * scale;
|
|
|
|
return {
|
|
width: newWidth, // 新的图片宽度
|
|
height: newHeight, // 新的图片高度
|
|
top, // 垂直方向偏移
|
|
left, // 水平方向偏移
|
|
scale // 缩放比例
|
|
} as xSIZEiMage;
|
|
}
|
|
export function translateScalePosition(orimg : xImageInfo, img : xSIZEiMage, mask : xSIZE, parent : xSIZE) {
|
|
let scaleinfo = scaleImageToFitFrame(orimg, mask)
|
|
img.width = scaleinfo.width
|
|
img.height = scaleinfo.height
|
|
img.top = scaleinfo.top
|
|
img.left = scaleinfo.left
|
|
img.scale = scaleinfo.scale
|
|
}
|
|
|
|
export function calculatePinchZoom(options : xCalculatePinchZoomOpts) : xSIZEiMage {
|
|
const {
|
|
initialPoints, // 初始双指坐标 [{x, y}, {x, y}]
|
|
currentPoints, // 当前双指坐标 [{x, y}, {x, y}]
|
|
originWidth, // 原始图片宽度
|
|
originHeight, // 原始图片高度
|
|
cropWidth, // 裁剪框宽度
|
|
cropHeight, // 裁剪框高度
|
|
width, // 当前图片宽度
|
|
height, // 当前图片高度
|
|
top, // 图片当前顶部位置
|
|
left, // 图片当前左侧位置
|
|
scale, // 当前缩放比例
|
|
initialScale // 初始缩放比例
|
|
} = options;
|
|
|
|
// 阻尼系数,0-1之间,越大越快,越小缩放越慢.
|
|
const DAMPING_FACTOR = 0.45;
|
|
|
|
// 计算初始和当前双指距离
|
|
const getDistance = (points : xPOINT[]) : number => {
|
|
const p1 = points[0];
|
|
const p2 = points[1];
|
|
return Math.sqrt(
|
|
Math.pow(p2.x - p1.x, 2) +
|
|
Math.pow(p2.y - p1.y, 2)
|
|
);
|
|
};
|
|
|
|
// 计算双指中心点
|
|
const getCenter = (points : xPOINT[]) : xPOINT => {
|
|
const p1 = points[0];
|
|
const p2 = points[1];
|
|
return {
|
|
x: (p1.x + p2.x) / 2,
|
|
y: (p1.y + p2.y) / 2
|
|
} as xPOINT;
|
|
};
|
|
|
|
const initialDistance = getDistance(initialPoints);
|
|
const currentDistance = getDistance(currentPoints);
|
|
if (initialDistance == currentDistance) return {
|
|
width,
|
|
height,
|
|
left,
|
|
top,
|
|
scale
|
|
} as xSIZEiMage;
|
|
// 计算缩放比例(加入阻尼)
|
|
const rawScaleChange = currentDistance / initialDistance;
|
|
const dampedScaleChange = 1 + (rawScaleChange - 1) * DAMPING_FACTOR;
|
|
let newScale = scale * dampedScaleChange;
|
|
|
|
// 计算最小宽高(确保至少覆盖裁剪框)
|
|
const minWidth = Math.max(cropWidth, originWidth * initialScale);
|
|
const minHeight = Math.max(cropHeight, originHeight * initialScale);
|
|
|
|
// 计算最大宽高
|
|
const maxWidth = originWidth * 3;
|
|
const maxHeight = originHeight * 3;
|
|
|
|
// 限制缩放范围
|
|
const MIN_SCALE = initialScale;
|
|
const MAX_SCALE = 3;
|
|
|
|
// 判断是否超过最大缩放限制
|
|
if (newScale > MAX_SCALE) {
|
|
return {
|
|
width: maxWidth,
|
|
height: maxHeight,
|
|
left,
|
|
top,
|
|
scale: MAX_SCALE
|
|
} as xSIZEiMage;
|
|
}
|
|
if (newScale < MIN_SCALE) {
|
|
return {
|
|
width: minWidth,
|
|
height: minHeight,
|
|
left,
|
|
top,
|
|
scale: MIN_SCALE
|
|
} as xSIZEiMage;
|
|
}
|
|
|
|
// 计算新的图片宽高
|
|
const newWidth = Math.max(minWidth, Math.min(originWidth * newScale, maxWidth));
|
|
const newHeight = Math.max(minHeight, Math.min(originHeight * newScale, maxHeight));
|
|
|
|
// 计算缩放中心点
|
|
const initialCenter = getCenter(initialPoints);
|
|
const currentCenter = getCenter(currentPoints);
|
|
|
|
// 计算偏移量(同样应用阻尼)
|
|
const rawOffsetX = (initialCenter.x - left) * (dampedScaleChange - 1);
|
|
const rawOffsetY = (initialCenter.y - top) * (dampedScaleChange - 1);
|
|
|
|
// 计算新的位置
|
|
const newLeft = left - rawOffsetX;
|
|
const newTop = top - rawOffsetY;
|
|
|
|
return {
|
|
width: newWidth,
|
|
height: newHeight,
|
|
left: newLeft,
|
|
top: newTop,
|
|
scale: newScale
|
|
} as xSIZEiMage;
|
|
}
|
|
|
|
// 根据裁剪框的缩放后的大小,与原始大小的缩放比,确定图片的当前大小的缩放比与位置
|
|
export function maskRatioScaleToPhotoSizePosition (oldMaskSize:xSIZEiMage,newMaskSize:xSIZE,oldimg:xImageInfo,nowImgPosSize:xSIZEiMage,index:number){
|
|
|
|
|
|
|
|
|
|
let difflen = oldMaskSize.width - newMaskSize.width
|
|
let scale2 = difflen/oldMaskSize.width
|
|
let nwdifflen = scale2*nowImgPosSize.width
|
|
let scale = nwdifflen / oldimg.width
|
|
if(nowImgPosSize.scale + scale>3){
|
|
scale = 3-nowImgPosSize.scale
|
|
}
|
|
let diffx = oldimg.width*scale
|
|
let diffy = oldimg.height*scale
|
|
|
|
|
|
let newWidth = nowImgPosSize.width + diffx
|
|
let newHeight = nowImgPosSize.height + diffy
|
|
nowImgPosSize.width = newWidth
|
|
nowImgPosSize.height = newHeight
|
|
|
|
// 左上角,应该是左上放大.
|
|
if(index == 0){
|
|
nowImgPosSize.left = nowImgPosSize.left - diffx
|
|
nowImgPosSize.top = nowImgPosSize.top - diffy
|
|
}
|
|
if(index == 1){
|
|
// nowImgPosSize.left = nowImgPosSize.left - diffx
|
|
nowImgPosSize.top = nowImgPosSize.top - diffy
|
|
}
|
|
if(index == 3){
|
|
nowImgPosSize.left = nowImgPosSize.left - diffx
|
|
// nowImgPosSize.top = nowImgPosSize.top - diffy
|
|
}
|
|
|
|
newMaskSize.width = oldMaskSize.width
|
|
newMaskSize.height = oldMaskSize.height
|
|
newMaskSize.left = oldMaskSize.left
|
|
newMaskSize.top = oldMaskSize.top
|
|
nowImgPosSize.scale = nowImgPosSize.scale + scale
|
|
}
|