js中如何实现自定义渐变函数的应用?

这是我很久以前所做的。 今天翻阅上一个演示时,突然发现了它。 我认为还不错。 分享它,每个人都将学习。

标题首先使这个演示的背景

我们公司生产的Webapp基于后端的图像滤镜,渐变和其他效果,也就是说,前端不负责渲染效果,而是将滤镜或渐变的参数发送到后端, 端,后端生成相应的效果图。效果图发送到前端,前端将效果图显示给用户。 通过所描述的过程,可以知道从前端到后端,然后从后端到前端,这将花费大量时间,并且此处理将对后端服务器造成很大压力。 要解决此问题,前端只能实现滤镜和渐变效果,但前提是前端和后端渲染效果必须保持一致。

问题是这样的。 由于问题已得到解决,因此请首先考虑编写演示以首先查看效果。 由于我们的项目基于webpack react redux,并且渲染选择了konvajs,这是一个基于Canvas开发的2d js框架库,您可以单击此处进入其官方网站。 然后,我们的演示也应该基于konvajs,以便以后移植到我们的应用程序中。 在检查konvajs API之后,我们发现它对过滤器的支持已经相当完善,但对渐变的支持仍然很弱。 不支持某些基本渐变,例如线性渐变和径向渐变。 由既然不支持我们只能考虑自己实现了,怎么实现呢?这确实难倒了我这个前端汪,在漫查资料无果的情况下我找后端大佬拿到了他们实现渐变的函数,代码如下:

//圆形渐变
private static IMOperation createCycleGradientIMOperation(Integer originalWidth, Integer originalHeight, Double midpoint) throws Exception {
    if (midpoint == null) {
        midpoint = 0.0;
    }
    if (midpoint < 0 || midpoint > 1) {
        throw new IllegalArgumentException("midpoint must be 0 to 1. midpoint:" + midpoint);
    }
    int opacityWidth, opacityHeight;
    opacityWidth = (int)(midpoint * originalWidth / 2);
    opacityHeight = (int)(midpoint * originalHeight / 2);
    IMOperation op = new IMOperation();
    op.openOperation();
    op.size(originalWidth, originalHeight);
    op.addRawArgs("xc:white");
    op.alpha("Set");
    op.channel("A");
    double dd;
    if (midpoint == 1) {
        dd = 1;
    } else {
        dd = 1.0 / (originalWidth / 2 - opacityWidth);
    }
    String expression = String.format("ot=h/w;xi=abs(i-(w/2));yj=abs(j-(h/2));xo=%d;yo=%d;rro=hypot(xo,yo);rr=hypot(w/2,h/2);dd=%f;w&gt;h?1-((sqrt(xi^2*ot^2+xi^2+(yj^2/ot^2)+yj^2))-rro)*dd:1-((sqrt(yj^2*ot^2+xi^2+(xi^2/ot^2)+yj^2))-rro)*dd", opacityWidth, opacityHeight, dd);
    op.fx(expression);
    op.closeOperation();
    op.addImage();
    op.compose("atop");
    op.composite();
    return op;
}
//矩形渐变
private static IMOperation createRectangleGradientIMOperation(Integer originalWidth, Integer originalHeight, Double midpoint) throws Exception {
    if (midpoint == null) {
        midpoint = 0.0;
    }
    if (midpoint < 0 || midpoint > 1) {
        throw new IllegalArgumentException("midpoint must be 0 to 1. midpoint:" + midpoint);
    }
    int opacityWidth, opacityHeight;
    opacityWidth = (int)(midpoint * originalWidth / 2);
    opacityHeight = (int)(midpoint * originalHeight / 2);
    IMOperation op = new IMOperation();
    op.openOperation();
    op.size(originalWidth, originalHeight);
    op.addRawArgs("xc:white");
    op.alpha("Set");
    op.channel("A");
    String expression = String.format("ot=h/w;xi=abs(i-(w/2));yj=abs(j-(h/2));xo=%d;yo=%d;rro=hypot(xo,yo);rr=hypot(w/2,h/2);dd=1/(rr-rro);yj&gt;xi*ot?1-((hypot(yj/ot,yj)-rro)*dd):1-((hypot(ot*xi,xi)-rro)*dd)", opacityWidth, opacityHeight);
    op.fx(expression);
    op.closeOperation();
    op.addImage();
    op.compose("atop");
    op.composite();
    return op;
}
//菱形渐变
private static IMOperation createDiamondGradientIMOperation(Integer originalWidth, Integer originalHeight, Double midpoint) throws Exception {
    if (midpoint == null) {
        midpoint = 0.0;
    }
    if (midpoint < 0 || midpoint > 1) {
        throw new IllegalArgumentException("midpoint must be 0 to 1. midpoint:" + midpoint);
    }
    int opacityWidth, opacityHeight;
    opacityWidth = (int)(midpoint * originalWidth / 2);
    opacityHeight = (int)(midpoint * originalHeight / 2);
    IMOperation op = new IMOperation();
    op.openOperation();
    op.size(originalWidth, originalHeight);
    op.addRawArgs("xc:white");
    op.alpha("Set");
    op.channel("A");
    double dd;
    if (midpoint == 1) {
        dd = 1;
    } else {
        dd = 1.0 / (originalWidth / 2 - opacityWidth);
    }
    String expression = String.format("ot=h/w;xi=abs(i-(w/2));yj=abs(j-(h/2));xo=%d;yo=%d;dd=%f;1-((xi+(yj/ot)-xo)*dd)", opacityWidth, opacityHeight, dd);
    op.fx(expression);
    op.closeOperation();
    op.addImage();
    op.compose("atop");
    op.composite();
    return op;
}

尽管代码的数量并不多,而且看起来很简单,但是一开始并不清楚,然后在我意识到这些代码中最关键的部分是表达式之前,我冷静下来考虑一下。 这个表达式是干什么用的? 实际上,它是处理每个像素,即通过某种算法改变每个像素的值以实现梯度。 实际上,这在我们理解之后更加简单。 我们只需要通过konvajs取出图像的像素数据,并使用上面表达式中的公式处理每个像素的数据,然后将旧像素数据替换为处理后的新像素数据。 最好检查官方网站的API以获取特定的API。 您可以查看文章末尾的演示,下面是我根据自己的理解转换的javascript代码并进行了简单的包装:

const gradientFixer = {
    radial: function(w, h, i, j, ow, oh, dd) {
        const ot = h / w;
        const xi = Math.abs(i - (w / 2));
        const yj = Math.abs(j - (h / 2));
        const xo = ow;
        const yo = oh;
        const rro = Math.hypot(xo, yo);
        const rr = Math.hypot(w / 2, h / 2);
        if (w > h) {
            return Math.round(255 * (1 - (Math.sqrt(Math.pow(xi, 2) * Math.pow(ot, 2) + Math.pow(xi, 2) + (Math.pow(yj, 2) / Math.pow(ot, 2)) + Math.pow(yj, 2)) - rro) * dd));
        } else {
            return Math.round(255 * (1 - ((Math.sqrt(Math.pow(yj, 2) * Math.pow(ot, 2) + Math.pow(xi, 2) + (Math.pow(xi, 2) / Math.pow(ot, 2)) + Math.pow(yj, 2))), rro) * dd));
        }
    },
    rect: function(w, h, i, j, ow, oh, dd) {
        const ot = h / w;
        const xi = Math.abs(i - (w / 2));
        const yj = Math.abs(j - (h / 2));
        const xo = ow;
        const yo = oh;
        const rro = Math.hypot(xo, yo);
        const rr = Math.hypot(w / 2, h / 2);
        if (yj > xi * ot) {
            return Math.round(255 * (1 - (Math.hypot(yj / ot, yj) - rro) * dd));
        } else {
            return Math.round(255 * (1 - (Math.hypot(ot * xi, xi) - rro) * dd));
        }
    },
    diamond: function(w, h, i, j, ow, oh, dd) {
        const ot = h / w;
        const xi = Math.abs(i - (w / 2));
        const yj = Math.abs(j - (h / 2));
        const xo = ow;
        const yo = oh;
        const rro = Math.hypot(xo, yo);
        const rr = Math.hypot(w / 2, h / 2);
        return Math.round(255 * (1 - ((xi + (yj / ot) - xo) * dd)));
    }
};
/**
 * 生成渐变效果
 * @param {[type]} imageData 像素数据
 * @param {[type]} midPoint 中心点
 * @param {String} type 渐变类型
 */
function gradient(imageData, midPoint, type = 'radial') {
    const { width, height } = imageData;
 
    const midpoint = midPoint || 0;
 
    if (midpoint < 0 || midpoint > 1) {
        throw new Error('midpoint must be 0 to 1. current midpoint is:' + midpoint);
    }
 
    const opacityWidth = Math.floor(midpoint * width / 2);
    const opacityHeight = Math.floor(midpoint * height / 2);
 
    const newImageData = new ImageData(width, height);
 
    let dd;
    if (midpoint === 1) {
        dd = 1;
    } else {
        dd = 1 / (width / 2 - opacityWidth);
    }
 
    const fixFunction = gradientFixer[type];
 
    if (fixFunction) {
        for (let i = 0, data = imageData.data; i < height; i++) {
            for (let j = 0; j < width; j++) {
                const index = i * width * 4 + j * 4,
                    r = data[index],
                    g = data[index + 1],
                    b = data[index + 2];
                const a = fixFunction(width, height, j, i, opacityWidth, opacityHeight, dd);
                imageData.data[index] = r;
                imageData.data[index + 1] = g;
                imageData.data[index + 2] = b;
                imageData.data[index + 3] = a;
            }
        }
    }
}

如果看懂了上面的代码了,后面的实现就很简单了,这里就不做赘述了哈。欢迎大家指错!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值