吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1301|回复: 8
收起左侧

[其他原创] [JavaScript] 图片颜色混合效果

[复制链接]
rimelight 发表于 2023-11-20 17:32
本帖最后由 rimelight 于 2023-11-20 21:09 编辑

由于内容比较少就全部塞到一个HTML文件里了,这不是一个好习惯,请勿模仿!


起因:
本人在一个项目中需要实现如下效果:将一个png格式的图片画到一个canvas上,其颜色根据玩家当前状态进行变化。
原本想法是在首次加载时进行预处理,存为多个离屏canvas,但后来添加颜色的过渡动画将此方法排除了(毕竟不可能将过渡时的这么多颜色全都存一份)


目标:
对于给定的任意颜色,能够高效的将该颜色叠加到图片上(毕竟要每帧渲染)


实现:
首先建一个简单的框架
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="utf-8">
    <title>test</title>
    <style>
        .bg {
            width: 600px;
            height: 600px;
            margin: 0;
            border: 1px solid #777;
        }
        label {
            margin: 10px;
        }
        canvas {
            background: #777;
        }
    </style>
</head>
<body style="display: flex; justify-content: center">
<div style="display: flex; flex-direction: column">
    <label>
        <span>R</span>
        <input id="red" type="range" min="0" max="255" value="128">
    </label>
    <label>
        <span>G</span>
        <input id="green" type="range" min="0" max="255" value="128">
    </label>
    <label>
        <span>B</span>
        <input id="blue" type="range" min="0" max="255" value="128">
    </label>
</div>
<div class="bg">
    <canvas id="game" width="600" height="600"></canvas>
</div>
</body>
<script>
function setColor() { //something }
</script>
</html>


将图片丢上去
圆内是不透明的(黑白色填充),圆外是透明的

测试用图片

测试用图片

[JavaScript] 纯文本查看 复制代码
const N = 600;
const canvas = document.getElementById("game"),
    ctx = canvas.getContext("2d");
let tree = new Image();
let r = 128, g = 128, b = 128;
tree.src = "tree.png";
tree.onload = () => draw();

function setColor(el) {
    let single = el.id, value = el.value;
    if (single === "red") r = value;
    else if (single === "green") g = value;
    else b = value;
    draw();
}

function draw() {
    ctx.drawImage(tree, 0, 0, N, N);
}


看起来只需要对draw函数进行修改就可以了,即设置要在绘制新形状时应用的合成操作的类型并填充颜色(毕竟是对当前图片进行“染色”)
对于globalCompositeOperation,请参考CanvasRenderingContext2D.globalCompositeOperation
经测试,采用multiply(正片叠底)的效果是最符合预期的
[JavaScript] 纯文本查看 复制代码
function draw() {
    ctx.save();
    ctx.clearRect(0, 0, N, N);
    ctx.drawImage(tree, 0, 0, N, N);
    ctx.globalCompositeOperation = "multiply";
    ctx.fillStyle = `rgb(${r},${g},${b})`;
    ctx.fillRect(0, 0, N, N);
    ctx.restore();
}


效果图1

效果图1

看下效果.....哎!不对!原本应该是透明的地方也被填上颜色了!
让我们看一下“正片叠底”:
其原理为对于两张图片在同一位置的像素点 (R1, G1, B1, A1)和(R2, G2, B2, A2) 进行如下运算
R = (R1 * R2) / 255
G、B同理
发现问题所在:按理说A1(Alpha)为0的图片参与运算后A = (0 * A2) / 255 = 0,原本是透明的地方也应该是透明的,但是!A的运算不符合这个公式!


所以,要对处理后图像的透明像素进行处理,source-atop或destination-atop可以完成这个需求,给出一种采用destination-atop的解决方案
[JavaScript] 纯文本查看 复制代码
function draw() {
    ctx.save();
    ctx.clearRect(0, 0, N, N);
    ctx.drawImage(tree, 0, 0, N, N);
    ctx.globalCompositeOperation = "multiply";
    ctx.fillStyle = `rgb(${r},${g},${b})`;
    ctx.fillRect(0, 0, N, N);
    ctx.globalCompositeOperation = "destination-atop";
    ctx.drawImage(tree, 0, 0, N, N);
    ctx.restore();
}


效果图2

效果图2


问题是解决了,但每次都要将同一图像画两次总给人一种冗余的感觉,而且明明“只要RGBA都符合公式”就能完美实现所需效果,所以,应该会有一个更好的解决方法吧?可惜我目前并不知道


此外,尝试采用不同的混合模式可以达到原本进行像素运算会十分麻烦的效果,比如采用lighter来还原一下东方神灵庙的特产——瞎眼光玉

效果图3

效果图3


附源码
code.zip (44.41 KB, 下载次数: 11)

免费评分

参与人数 2吾爱币 +8 热心值 +2 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
yjn866y + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

kkblog 发表于 2023-11-20 20:09
学习一哈
yjn866y 发表于 2023-11-21 09:52
yinuo2012 发表于 2023-11-21 11:21
qingfeng2288 发表于 2023-11-21 11:49
学习,感谢分享!
wfghim 发表于 2023-11-22 08:58
这种js代码,能用AI写出来吗?有没有大佬知道的
 楼主| rimelight 发表于 2023-11-22 10:05
wfghim 发表于 2023-11-22 08:58
这种js代码,能用AI写出来吗?有没有大佬知道的

或许可以,不过我让AI帮我解决然后它给出了一个不怎么样的方案(将fillStyle设置为半透明的颜色然后糊上去),当然也有可能是我描述太差的问题
daimt 发表于 2023-11-24 10:36
很不错的帖子……
eaglexiong 发表于 2023-11-24 15:11
感谢分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 17:42

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表