画布上的绘画随着时间的流逝而褪色|奇怪的 alpha 分层行为

2023-12-15

我正在一块未被清除的画布上绘画,并使其随着时间的推移画布逐渐褪色为纯色,或者逐渐褪色以显示后面的图层。

我的第一直觉是简单地在每帧中用低 Alpha 值填充绘图上的一个矩形,以便填充颜色逐渐累积,使绘图逐渐淡出。

但我发现了一些奇怪的行为(至少对我来说,我确信这是有原因的)。 填充颜​​色永远不会完全累积。结果会根据油漆和填充颜色是否彼此较浅/较深而变化。

我发现这个问题有人和我做同样的事情:绘制画布后淡出线条?

最上面的答案看起来不错,和我尝试过的一样。但它只适用于白底黑字。这是同一小提琴的另一个版本,具有不同的颜色,你会看到图画永远不会消失,它留下了鬼魂:http://jsfiddle.net/R4V97/92/

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d"),
    painting = false,
    lastX = 0,
    lastY = 0;

canvas.width = canvas.height = 600;

canvas.onmousedown = function (e) {
    if (!painting) {
        painting = true;
    } else {
        painting = false;
    }

    lastX = e.pageX - this.offsetLeft;
    lastY = e.pageY - this.offsetTop;
};

canvas.onmousemove = function (e) {
    if (painting) {
        mouseX = e.pageX - this.offsetLeft;
        mouseY = e.pageY - this.offsetTop;

        ctx.strokeStyle = "rgba(255,255,255,1)";
        ctx.beginPath();
        ctx.moveTo(lastX, lastY);
        ctx.lineTo(mouseX, mouseY);
        ctx.stroke();

        lastX = mouseX;
        lastY = mouseY;
    }
}

function fadeOut() {
    ctx.fillStyle = "rgba(60,30,50,0.2)";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    setTimeout(fadeOut,100);
}

fadeOut();

此外,如果您将填充不透明度更改为 0.01,并将计时更改为 20 毫秒,它甚至永远不会填充正确的颜色,使其变成灰色。

我尝试过的其他事情都遇到了同样的根本问题。我尝试过在两个画布之间弹跳,先使用画布 A 并用减小的 alpha 值将其绘制到画布 B,然后再将画布 B 绘制回画布 A - 同样的问题,存在一个不会消失的阈值。

作为测试,我什至尝试了获取图像数据的超慢方法,循环遍历所有像素 Alpha 通道并乘以 0.95,然后再将数据放回。它仍然留下了一个幽灵,我必须在循环中做这样的事情(由于某种原因它甚至永远不会低于 10):

if (alpha<25) {
    alpha = 0;
}

我想我也许能够将画布划分为网格或行,并在每帧一个单元格中执行 imageData 操作,在淡入淡出时间较短的情况下,它可能不会引人注目。

但如果有人知道更好的方法或者我没有得到的核心内容是什么,我将非常感激!

  • 哦还应该注意,我让它在画布上栅格化,因为我正在使用粒子/算法进行绘画,所以我不是在寻找意味着我不断刷新和重绘相同点的解决方案。谢谢!

RGB 和 8 位整数数学!

您需要避免接触 RGB 通道,因为当您对 8 位值进行数学计算时,结果将出现巨大错误。例如(8 位整数数学) 14 * 0.1 = 1, 8 * 0.1 = 1 因此,当您在现有像素上绘制时,您将得到一个舍入误差,该误差对于每个通道来说都是不同的,具体取决于您在顶部绘制的颜色。

没有完美的解决方案,但您可以通过使用全局复合操作“destination-out”来避免颜色通道并仅淡出 alpha 通道,这将通过减少像素 alpha 来淡出渲染。

对于淡入率低至 globalAlpha = 0.01 甚至稍低一点的 0.006 效果很好,但低于该值可能会很麻烦。然后,如果您需要更慢的淡入淡出,只需每隔第二或第三帧进行淡入淡出即可。

ctx.globalAlpha = 0.01;           // fade rate
ctx.globalCompositeOperation = "destination-out"  // fade out destination pixels
ctx.fillRect(0,0,w,h)
ctx.globalCompositeOperation = "source-over"
ctx.globalAlpha = 1;           // reset alpha

请注意,这会将画布淡化为透明。如果您希望淡入淡出过程向特定颜色发展,则需要将淡入淡出画布保留为单独的屏幕外画布,并将其绘制在具有所需淡入背景的画布上。

演示彩色背景上的彩色粒子和淡入淡出。

var canvas = document.createElement("canvas");
canvas.width = 1024;
canvas.height = 1024;
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
document.body.appendChild(canvas);

var fadCan = document.createElement("canvas");
fadCan.width = canvas.width;
fadCan.height = canvas.height;
var fCtx = fadCan.getContext("2d");

var cw = w / 2;  // center 
var ch = h / 2;
var globalTime;

function randColour(){
    return "hsl("+(Math.floor(Math.random()*360))+",100%,50%)";
}
var pps = [];
for(var i = 0; i < 100; i ++){
    pps.push({
        x : Math.random() * canvas.width,
        y : Math.random() * canvas.height,
        d : Math.random() * Math.PI * 2,
        sp : Math.random() * 2 + 0.41,
        col : randColour(),
        s : Math.random() * 5 + 2,
        t : (Math.random() * 6 -3)/10,
        
    });
}
function doDots(){
    for(var i = 0; i < 100; i ++){
        var d = pps[i];
        d.d += d.t * Math.sin(globalTime / (d.t+d.sp+d.s)*1000);
        d.x += Math.cos(d.d) * d.sp;
        d.y += Math.sin(d.d) * d.sp;
        d.x = (d.x + w)%w;
        d.y = (d.y + w)%w;
        fCtx.fillStyle = d.col;
        fCtx.beginPath();
        fCtx.arc(d.x,d.y,d.s,0,Math.PI * 2);
        fCtx.fill();
        
    }
}


var frameCount = 0;
// main update function
function update(timer){
    globalTime = timer;
    frameCount += 1;
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
    ctx.fillStyle = "hsl("+(Math.floor((timer/50000)*360))+",100%,50%)";
    ctx.fillRect(0,0,w,h);
    doDots();
    if(frameCount%2){
        fCtx.globalCompositeOperation = "destination-out";
        fCtx.fillStyle = "black";
        var r = Math.random() * 0.04
        fCtx.globalAlpha = (frameCount & 2 ? 0.16:0.08)+r;
        fCtx.fillRect(0,0,w,h);
        fCtx.globalAlpha = 1;
        fCtx.globalCompositeOperation = "source-over"
    }
    ctx.drawImage(fadCan,0,0)
    requestAnimationFrame(update);
}
requestAnimationFrame(update);

演示在带有淡入淡出的彩色背景上绘图。

单击拖动鼠标进行绘制。

var canvas = document.createElement("canvas");
canvas.width = 1024;
canvas.height = 1024;
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
document.body.appendChild(canvas);

var fadCan = document.createElement("canvas");
fadCan.width = canvas.width;
fadCan.height = canvas.height;
var fCtx = fadCan.getContext("2d");

var cw = w / 2;  // center 
var ch = h / 2;
var globalTime;

function randColour(){
    return "hsl("+(Math.floor(Math.random()*360))+",100%,50%)";
}



// main update function
function update(timer){
    globalTime = timer;
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
    ctx.fillStyle = "hsl("+(Math.floor((timer/150000)*360))+",100%,50%)";
    ctx.fillRect(0,0,w,h);
    if(mouse.buttonRaw === 1){
        fCtx.strokeStyle = "White";
        fCtx.lineWidth = 3;
        fCtx.lineCap = "round";
        fCtx.beginPath();
        fCtx.moveTo(mouse.lx,mouse.ly);
        fCtx.lineTo(mouse.x,mouse.y);
        fCtx.stroke();
    }


    mouse.lx = mouse.x;
    mouse.ly = mouse.y;
    fCtx.globalCompositeOperation = "destination-out";
    fCtx.fillStyle = "black";
    fCtx.globalAlpha = 0.1;
    fCtx.fillRect(0,0,w,h);
    fCtx.globalAlpha = 1;
    fCtx.globalCompositeOperation = "source-over"
    ctx.drawImage(fadCan,0,0)
    requestAnimationFrame(update);
}


var mouse = (function () {
    function preventDefault(e) {
        e.preventDefault();
    }
    var mouse = {
        x : 0,
        y : 0,
        w : 0,
        alt : false,
        shift : false,
        ctrl : false,
        buttonRaw : 0,
        over : false,
        bm : [1, 2, 4, 6, 5, 3],
        active : false,
        bounds : null,
        crashRecover : null,
        mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
    };
    var m = mouse;
    function mouseMove(e) {
        var t = e.type;
        m.bounds = m.element.getBoundingClientRect();
        m.x = e.pageX - m.bounds.left + scrollX;
        m.y = e.pageY - m.bounds.top + scrollY;
        m.alt = e.altKey;
        m.shift = e.shiftKey;
        m.ctrl = e.ctrlKey;
        if (t === "mousedown") {
            m.buttonRaw |= m.bm[e.which - 1];
        } else if (t === "mouseup") {
            m.buttonRaw &= m.bm[e.which + 2];
        } else if (t === "mouseout") {
            m.buttonRaw = 0;
            m.over = false;
        } else if (t === "mouseover") {
            m.over = true;
        } else if (t === "mousewheel") {
            m.w = e.wheelDelta;
        } else if (t === "DOMMouseScroll") {
            m.w = -e.detail;
        }
        if (m.callbacks) {
            m.callbacks.forEach(c => c(e));
        }
        if ((m.buttonRaw & 2) && m.crashRecover !== null) {
            if (typeof m.crashRecover === "function") {
                setTimeout(m.crashRecover, 0);
            }
        }
        e.preventDefault();
    }
    m.addCallback = function (callback) {
        if (typeof callback === "function") {
            if (m.callbacks === undefined) {
                m.callbacks = [callback];
            } else {
                m.callbacks.push(callback);
            }
        }
    }
    m.start = function (element) {
        if (m.element !== undefined) {
            m.removeMouse();
        }
        m.element = element === undefined ? document : element;
        m.mouseEvents.forEach(n => {
            m.element.addEventListener(n, mouseMove);
        });
        m.element.addEventListener("contextmenu", preventDefault, false);
        m.active = true;
    }
    m.remove = function () {
        if (m.element !== undefined) {
            m.mouseEvents.forEach(n => {
                m.element.removeEventListener(n, mouseMove);
            });
            m.element.removeEventListener("contextmenu", preventDefault);
            m.element = m.callbacks = undefined;
            m.active = false;
        }
    }
    return mouse;
})();

mouse.start(canvas);
requestAnimationFrame(update);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

画布上的绘画随着时间的流逝而褪色|奇怪的 alpha 分层行为 的相关文章

随机推荐

  • Bootstrap 4 - 粘性页脚 - 动态页脚高度

    我需要在我的页面上放置一个粘性页脚 但是我没有为页脚设置明确的高度 在较小的屏幕上 行的大小会调整 页脚会变长 因此 getbootstrap 上提供的默认粘性页脚示例不起作用 因为它需要固定的页脚高度 有什么方法可以实现这个吗 Stick
  • 使用 Apache poi 从 docx 获取文本样式

    我正在尝试从 MS docx 文件中获取样式信息 使用添加的样式 如粗体 斜体 编写文件内容没有问题 字体大小等 但读取文件内容并获取样式信息不太清楚 我尝试过使用 XWPFDocument 这个 API 似乎没有读取样式的能力 我现在正在
  • 如何在使用 swift 继续访问视图控制器之前实例化并加载视图控制器

    当我从 viewController A 转到 viewController B 时 我遇到了一个问题 在继续执行之前大约有 5 秒的延迟 我相信这是由于我在 viewDidLoad 中加载的视图数量所致 我有一个 xib 文件 其中包含代
  • 我可以在电子邮件中嵌入 Twitter 源吗?

    我即将向我的客户发送一封电子邮件活动 电子邮件将以 HTML 格式发送 我想在电子邮件中嵌入 Twitter 提要 类似于您在博客和网站中看到的嵌入 Twitter 小部件 当我复制并粘贴通过 Twitter 网站生成的代码时 我的电子邮件
  • 如何使用单声道解决 OSX 上的 SecureChannelFailure

    我正在尝试访问https geocoder cit api here com在单声道上使用 NET F 我正在使用Http帮手来自FSharp Data使用以下代码 let baseUrl https geocoder cit api he
  • 每个奇特的指针都应该是迭代器吗?

    我正在为 C 开发基于段的内存分配器 在这个分配器中 当你释放一块内存时 你必须知道是哪块内存segment它来自 因此 我将指向该段的指针存储为花哨的成员pointer从分配器返回allocate功能 只是为了显示我正在谈论的界面 这是f
  • Android - GCM 服务器密钥不适用于 Urban Airship

    我正在尝试将 Urban Airship 推送通知服务集成到我的 android 项目中 我在 google 开发者控制台中创建了一个新项目 并创建了一个 GCM 服务器密钥 我也在 Urban Airship 配置中添加了该 GCM 密钥
  • 使用实体框架我想仅包含第一个子对象而不是子对象的子对象(子的子对象)

    使用实体框架我想仅包含第一级子对象而不是子对象的子对象 我有这两门课 public class BusinessesTBL public string ID get set public string FirstName get set p
  • 从基类到所有后代遍历类层次结构

    在我正在编写的 iOS 应用程序中 我想遍历类层次结构以列出所有子类的清单 我的意图是使用每个子类类型作为键 viaNSStringForClass 在字典里 我的动机是能够自动发现基类的所有变体 以便我可以调用与该类关联的方法 出于分工的
  • 如何使用下拉列表而不是所有者绘制样式更改组合框控件的外观高度?

    背景 在某个对话框中 有多个连续的控件 我希望它们都具有相同的表观高度 但是 对于具有 CBS DROPDOWNLIST 样式且没有 CBS OWNERDRAW 的组合框 我在更改表观高度时遇到了麻烦 问题 如何更改此类 DROPDOWNL
  • Slim 4 将所有路由放入控制器中,无需 $app

    我需要将所有注册的路由放入控制器中 在 slim 3 中 可以使用以下方式获得路由器 router container gt get router routes router gt getRoutes 使用 app 很容易 routes a
  • 可以在 C# 中使用带有非默认构造函数的单例吗?

    我正在为我的一个项目实施一个通知框架 因为我希望它非常通用 所以用户可以使用多个传输层 这样他就不需要真正关心使用一种交付方法 比如说 WCF 或另一种交付方法 例如 ActiveMQ 用户访问的接口当然与交付方法 WCF 或 Active
  • Doc 文件无法从 asp.net 中的 UserControl 下载

    我有用户控件 其中包含带有候选数据的网格 有一个带有模板字段链接按钮的列候选名称 我附加了一个 rowcommand 事件 我正在该事件上下载一个 word 文件 我有下载文档文件代码 可以从简单的网页下载我的文档文件 但此代码不适用于用户
  • 在最后一帧停止 CSS3 动画

    我有一个 4 部分的 CSS3 动画 点击时播放 但动画的最后一部分是为了将其从屏幕上移开 然而 一旦播放完毕 它总是会回到原来的状态 谁知道我怎样才能将其停止在最后一个 CSS 框架上 100 或者如何在播放后删除它所在的整个 div k
  • 在命令行上使用 OSX Leaks 进行 C 程序?

    谁能给我任何运行 OSX 的简单语法leaks用于检测已编译的 C 程序中的内存泄漏的工具 我使用的是 10 8 所以我遇到了严重的兼容性问题valgrind 我读过的关于 XCode 的泄漏 工具的大部分内容都涉及 XCode 环境 这不
  • Typescript 中的方括号与表达式后的关系

    Typescript 中以下两个方括号符号有区别吗 尝试了几个场景 看起来它们是等效的 谢谢你 interface test a string b string const x test a aaaa b bbbb const y test
  • Vincent 图没有显示,没有收到错误

    由于某种原因 当我尝试与文森特一起绘制某些内容时 我得到以下内容
  • 将复选框状态传递给 PHP

  • Java将文件附加到zip中[重复]

    这个问题在这里已经有答案了 可能的重复 使用 Java 将文件附加到 zip 文件 我有一个 zip 文件 其中包含一些文件夹 但重要的一个是 dir 其中有另一个名为 文件夹 的文件夹 该文件夹包含许多我需要能够更新的文件 我现在在 zi
  • 画布上的绘画随着时间的流逝而褪色|奇怪的 alpha 分层行为

    我正在一块未被清除的画布上绘画 并使其随着时间的推移画布逐渐褪色为纯色 或者逐渐褪色以显示后面的图层 我的第一直觉是简单地在每帧中用低 Alpha 值填充绘图上的一个矩形 以便填充颜色逐渐累积 使绘图逐渐淡出 但我发现了一些奇怪的行为 至少