拿到一张图片,指定一个圆,根据圆心即原点,移动到 移动点后的操作。
将圆内其他像素同步偏移。
将原点和移动点相连后,垂直这段线做一条直径。
连接移动点和直径与圆的两个交点,如图灰色线段,将圆划分为两个区域。
大的区域,即原来的上半圆 里面的像素 径向拉伸到多出一个三角形。
小的区域,即原来的下半圆 里面的像素 径向压缩。
历遍圆内的所有像素,根据以上逻辑,可以找到该点在原来的坐标,复制像素即可。
根据上述逻辑,使用html的canvas编写js代码即可。主要函数如下:
function liquefying(sx, sy, r, dx, dy, data) {
// console.log("直径线段方程",`y = ${直径斜率}*x +${直径偏移}`)
var myImage = ctx.createImageData(len, len);
var myImageData = myImage.data;if (sx == dx && sy == dy) {
// 原始图片不用修改,返回null
return null
}
let 斜率 = null;if (dx != sx) {
斜率 = (dy - sy) / (dx - sx);
}
let 偏移 = 斜率 == null? null : sy - (斜率 * sx)
// console.log("移动线段方程",`y = ${斜率}*x +${偏移}`)let 直径斜率 = 斜率 == null ? 0 : -1 / 斜率
let 直径偏移 = 斜率 == null ? sy : sy - (直径斜率 * sx)let 移动的距离 = measurePointLen(sx, sy, dx, dy)
// console.log(斜率,偏移,直径斜率,直径偏移)
let i = 0
for (let y = 0; y < len; y++) {
for (let x = 0; x < len; x++) {
// 超出圆范围的不变,距离圆的距离大于r不变
let 本点到圆心的距离 = measurePointLen(x, y, sx, sy)
var newIndex = (x + (y * len)) * 4;if (本点到圆心的距离 >= r) {
// console.log(x,y,targetY,"newIndex",newIndex,"i",i)
myImageData[newIndex] = data[newIndex];
myImageData[newIndex + 1] = data[newIndex + 1];
myImageData[newIndex + 2] = data[newIndex + 2];
myImageData[newIndex + 3] = data[newIndex + 3];
}
else {
// 圆范围内的通过落点位置,计算原始位置
// 根据给定点偏移的方向,计算是 压缩和拉伸的 分割点
// 找到该点在直径上的落点,为直径和移动线平行线的交点let 移动线平行线偏移 = 斜率==null? null: y - (斜率 * x)
let 落点x = 斜率==null? x :(移动线平行线偏移 - 直径偏移) / (直径斜率 - 斜率)
let 落点y = 斜率==null? sy :落点x * 直径斜率 + 直径偏移
// console.log("移动线平行线 线段方程",`y = ${斜率}*x +${移动线平行线偏移}`)
// console.log(x,落点x,"落点x,落点y",y,落点y)
// 计算该点 和 落点的距离
let 本点到落点的距离 = measurePointLen(x, y, 落点x, 落点y)// 计算该落点上最大距离(相似三角形)
let 落点到圆心距离 = measurePointLen(sx, sy, 落点x, 落点y)
let maxLen = 移动的距离 * (r - 落点到圆心距离) / r// 如果是移动点反方向的半圆内,肯定是拉伸
let 本点到移动后点的距离 = measurePointLen(x, y, dx, dy)
let 拉伸 = (Math.sqrt(本点到圆心的距离 * 本点到圆心的距离 + 移动的距离 * 移动的距离) < 本点到移动后点的距离)
|| (本点到落点的距离 < maxLen)
if (拉伸) {
// 拉伸,固定点为圆的边另一头
let 原始长度 = Math.sqrt(r * r - 落点到圆心距离 * 落点到圆心距离)
let 拉伸到的长度 = 原始长度 + maxLen;
// 圆的方程和 移动线平行线交点
let 圆和移动线平行线交点array = 计算直线和圆的交点(sx, sy, r, 斜率, 移动线平行线偏移, x)
// 取离 移动后点 最远的点
let 点1距离移动后点距离 = measurePointLen(圆和移动线平行线交点array[0].x, 圆和移动线平行线交点array[0].y, dx, dy)
let 圆的方程和移动线平行线交点 = 圆和移动线平行线交点array[0]
if (点1距离移动后点距离 < r) {
圆的方程和移动线平行线交点 = 圆和移动线平行线交点array[1]
}
// 交点距离 本点 距离
let 交点距离本点距离 = measurePointLen(圆的方程和移动线平行线交点.x, 圆的方程和移动线平行线交点.y, x, y)
let 缩放前距离交点距离 = 原始长度 * 交点距离本点距离 / 拉伸到的长度let points = 计算直线和圆的交点(圆的方程和移动线平行线交点.x,
圆的方程和移动线平行线交点.y,
缩放前距离交点距离,
斜率,
移动线平行线偏移,
x)
// 取离 移动后点 最远的点
let l1 = measurePointLen(points[0].x, points[0].y, sx, sy)
let point = points[0];
if (l1 > r) {
point = points[1]}
var sIndex = (point.x + (point.y * len)) * 4;
// console.log("找到原始点" , point.x,point.y, sIndex)
myImageData[newIndex] = data[sIndex];
myImageData[newIndex + 1] = data[sIndex + 1];
myImageData[newIndex + 2] = data[sIndex + 2];
myImageData[newIndex + 3] = data[sIndex + 3];
} else {
// 压缩,固定点为圆的边// 原始长度
let 原始长度 = Math.sqrt(r * r - 落点到圆心距离 * 落点到圆心距离)
let 压缩到的长度 = 原始长度 - maxLen;
// 圆的方程和 移动线平行线交点
let 圆和移动线平行线交点array = 计算直线和圆的交点(sx, sy, r, 斜率, 移动线平行线偏移,x)
// 取离 移动后点 最近的点
let 点1距离移动后点距离 = measurePointLen(圆和移动线平行线交点array[0].x, 圆和移动线平行线交点array[0].y, dx, dy)
let 圆和移动线平行线交点 = 圆和移动线平行线交点array[0]
if (点1距离移动后点距离 > r) {
圆和移动线平行线交点 = 圆和移动线平行线交点array[1]
}
// 交点距离 本点 距离
let 交点距离本点距离 = measurePointLen(圆和移动线平行线交点.x, 圆和移动线平行线交点.y, x, y)
let 缩放前距离交点距离 = 原始长度 * 交点距离本点距离 / 压缩到的长度
let points = 计算直线和圆的交点(圆和移动线平行线交点.x,圆和移动线平行线交点.y,缩放前距离交点距离, 斜率, 移动线平行线偏移,x)
// 如果该点与圆心 距离超过R 表示为另外一个点
let l1 = measurePointLen(points[0].x, points[0].y, sx, sy)
let point = points[0];
if (l1 > r) {
point = points[1]}
var sIndex = (point.x + (point.y * len)) * 4;
// console.log("找到原始点" , point.x,point.y, sIndex)myImageData[newIndex] = data[sIndex];
myImageData[newIndex + 1] = data[sIndex + 1];
myImageData[newIndex + 2] = data[sIndex + 2];
myImageData[newIndex + 3] = data[sIndex + 3];
}
}
}
}
return myImage
}
根据以上代码 和 参数 sx: 125, sy: 125, r: 100,dx:150, dy:150,得到效果(左结果图,右原图) 如下:
增加定时器,改变移动点的位置,可以得到一个动态图片。