前端图片液化(圆内像素根据原点移动整体偏移)

2023-09-16 14:50


拿到一张图片,指定一个圆,根据圆心即原点,移动到 移动点后的操作。

将圆内其他像素同步偏移。

将原点和移动点相连后,垂直这段线做一条直径。

连接移动点和直径与圆的两个交点,如图灰色线段,将圆划分为两个区域。

大的区域,即原来的上半圆 里面的像素 径向拉伸到多出一个三角形。

小的区域,即原来的下半圆 里面的像素 径向压缩。

历遍圆内的所有像素,根据以上逻辑,可以找到该点在原来的坐标,复制像素即可。

根据上述逻辑,使用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,得到效果(左结果图,右原图) 如下:

增加定时器,改变移动点的位置,可以得到一个动态图片。


# 图片处理