我正在绘制一个未被清除的 Canvas ,并使 Canvas 随着时间的推移逐渐变成纯色,或者在 alpha 中逐渐消失以显示后面的图层。
我的第一直觉是简单地用每帧的低 alpha 在绘图上填充一个矩形,这样填充颜色就会逐渐累积并淡出绘图。
但我发现了一些奇怪的行为(至少对我来说,我确信这是有原因的)。 填充颜色永远不会完全累积。结果会根据油漆和填充颜色是否比彼此更亮/更暗而变化。
我发现这个问题有人和我做同样的事情:fade out lines after drawing canvas?
顶部的答案看起来不错,和我试过的一样。但它只适用于白底黑字。这是同一个 fiddle 的不同颜色的另一个版本,你会看到绘图永远不会消失,它留下了一个幽灵: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 毫秒,它甚至不会填充正确的颜色,而是保持灰色。
我尝试过的其他事情都存在同样的根本问题。我试过在两个 Canvas 之间弹跳,使用 Canvas A 并使用减少的 alpha 将其绘制到 Canvas B,然后再将 Canvas B 画回 Canvas A - 同样的问题,有一个阈值不会消失。
作为测试,我什至尝试了获取图像数据的超慢操作,遍历所有像素 alpha channel 并乘以 0.95,然后再将数据放回原处。它仍然留下了阴影,我必须在循环中做这样的事情(由于某种原因它甚至从未低于 10):
if (alpha<25) {
alpha = 0;
}
我在想我也许可以将 Canvas 分成一个网格或几行,并在每帧一个单元格中执行 imageData 操作,这在低淡入淡出时间时可能不会引人注意。
但是,如果有人知道更好的方法或者我没有得到的核心内容是什么,我将不胜感激!
最佳答案
您需要避免接触 RGB channel ,因为当您对 8 位值进行数学运算时,结果会出现巨大错误。例如(8 位整数数学)14 * 0.1 = 1、8 * 0.1 = 1 因此,当您在现有像素上绘制时,您将得到一个舍入误差,该误差对于每个 channel 都不同,具体取决于您在顶部绘制的颜色。
没有完美的解决方案,但您可以通过使用全局复合操作“destination-out”来避免颜色 channel 并仅淡化 alpha channel 这将通过减少像素 alpha 来淡出渲染。
适用于低至 globalAlpha = 0.01 甚至更低一点的 0.006 的淡入淡出率,但低于该值可能会很麻烦。然后,如果您需要更慢的淡入淡出,只需每第 2 或第 3 帧进行一次淡入淡出。
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
请注意,这会将 Canvas 淡化为透明。如果您希望渐变朝着特定颜色发展,您需要将渐变 Canvas 保持为单独的屏幕外 Canvas ,并将其绘制在具有要渐变到的所需背景的 Canvas 上。
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);
关于javascript - 随时间褪色的 Canvas 绘画 |奇怪的阿尔法分层行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41483806/
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查
这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build
我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s
我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio
我在一段非常简单的代码(如我所想)中得到了一个错误的值:org=4caseorgwhenorg=4val='H'endputsval=>nil请不要生气,我希望我错过了一些非常明显的东西,但我真的想不通。谢谢。 最佳答案 这是典型的Ruby错误。case有两种被调用的方法,一种是你传递一个东西作为分支的基础,另一种是你不传递的东西。如果您确实在case中指定了一个表达式语句然后评估所有其他条件并与===进行比较.在这种情况下org评估为false和org===false显然不是真的。所有其他情况也是如此,它们要么是真的,要么是假的。
假设您在Ruby中执行此操作:ar=[1,2]x,y=ar然后,x==1和y==2。是否有一种方法可以在我自己的类中定义,从而产生相同的效果?例如rb=AllYourCode.newx,y=rb到目前为止,对于这样的赋值,我所能做的就是使x==rb和y=nil。Python有这样一个特性:>>>classFoo:...def__iter__(self):...returniter([1,2])...>>>x,y=Foo()>>>x1>>>y2 最佳答案 是的。定义#to_ary。这将使您的对象被视为要分配的数组。irb>o=Obje
因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实
我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的