当使用 Canvas 应用转换时,生成的文本也(显然)被转换。有没有办法防止某些影响文本的转换,例如反射?
例如,我设置了一个全局变换矩阵,使 Y 轴指向上方,X 轴指向右侧,(0, 0) 点位于屏幕中心 (您对数学坐标系的期望)。
但是,这也会使文本颠倒。
const size = 200;
const canvas = document.getElementsByTagName('canvas')[0]
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.setTransform(1, 0, 0, -1, size / 2, size / 2);
const triangle = [
{x: -70, y: -70, label: 'A'},
{x: 70, y: -70, label: 'B'},
{x: 0, y: 70, label: 'C'},
];
// draw lines
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.moveTo(triangle[2].x, triangle[2].y);
triangle.forEach(v => ctx.lineTo(v.x, v.y));
ctx.stroke();
ctx.closePath();
// draw labels
ctx.textAlign = 'center';
ctx.font = '24px Arial';
triangle.forEach(v => ctx.fillText(v.label, v.x, v.y - 8));<canvas></canvas>
除了手动重置变换矩阵之外,是否有一种“智能”的方式让文本处于“正确”的方向?
最佳答案
要以 Tai 的回答为基础,这太棒了,您可能需要考虑以下几点:
const size = 200;
const canvas = document.getElementsByTagName('canvas')[0]
canvas.width = canvas.height = size;
const ctx = canvas.getContext('2d');
// Create a custom fillText funciton that flips the canvas, draws the text, and then flips it back
ctx.fillText = function(text, x, y) {
this.save(); // Save the current canvas state
this.scale(1, -1); // Flip to draw the text
this.fillText.dummyCtx.fillText.call(this, text, x, -y); // Draw the text, invert y to get coordinate right
this.restore(); // Restore the initial canvas state
}
// Create a dummy canvas context to use as a source for the original fillText function
ctx.fillText.dummyCtx = document.createElement('canvas').getContext('2d');
ctx.setTransform(1, 0, 0, -1, size / 2, size / 2);
const triangle = [
{x: -70, y: -70, label: 'A'},
{x: 70, y: -70, label: 'B'},
{x: 0, y: 70, label: 'C'},
];
// draw lines
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.moveTo(triangle[2].x, triangle[2].y);
triangle.forEach(v => ctx.lineTo(v.x, v.y));
ctx.stroke();
ctx.closePath();
// draw labels
ctx.textAlign = 'center';
ctx.font = '24px Arial';
// For this particular example, multiplying x and y by small factors >1 offsets the labels from the triangle vertices
triangle.forEach(v => ctx.fillText(v.label, 1.2*v.x, 1.1*v.y));
如果对于您的实际应用程序,上面的内容很有用,您将在绘制非文本对象和绘制文本之间来回切换并且不想记住来回翻转 Canvas 。 (在当前的示例中这不是一个大问题,因为您绘制三 Angular 形然后绘制所有文本,所以您只需要翻转一次。但是如果您有一个更复杂的不同应用程序,那可能会很烦人。)在上面的示例中,我将 fillText 方法替换为自定义方法,该方法翻转 Canvas 、绘制文本,然后再次将其翻转回来,这样您就不必在每次绘制文本时都手动执行此操作。
结果:
如果您不喜欢覆盖默认的fillText,那么显然您可以创建一个具有新名称的方法;这样您也可以避免创建虚拟上下文,而只需在您的自定义方法中使用 this.fillText。
编辑:上述方法也适用于任意缩放和平移。 scale(1, -1) 只是在 x 轴上反射(reflect)了 Canvas :在这个转换之后,位于 (x, y) 的点现在将位于 (x, -y)。无论平移和缩放如何,都是如此。如果您希望无论缩放如何,文本都保持恒定大小,那么您只需通过缩放来缩放字体大小。例如:
<html>
<body>
<canvas id='canvas'></canvas>
</body>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
var framesPerSec = 100;
var msBetweenFrames = 1000/framesPerSec;
ctx.font = '12px Arial';
function getRandomCamera() {
return {x: ((Math.random() > 0.5) ? -1 : 1) * Math.random()*5,
y: ((Math.random() > 0.5) ? -1 : 1) * Math.random()*5+5,
zoom: Math.random()*20+0.1,
};
}
var camera = getRandomCamera();
moveCamera();
function moveCamera() {
var newCamera = getRandomCamera();
var transitionFrames = Math.random()*500+100;
var animationTime = transitionFrames*msBetweenFrames;
var cameraSteps = { x: (newCamera.x-camera.x)/transitionFrames,
y: (newCamera.y-camera.y)/transitionFrames,
zoom: (newCamera.zoom-camera.zoom)/transitionFrames };
for (var t=0; t<animationTime; t+=msBetweenFrames) {
window.setTimeout(updateCanvas, t);
}
window.setTimeout(moveCamera, animationTime);
function updateCanvas() {
camera.x += cameraSteps.x;
camera.y += cameraSteps.y;
camera.zoom += cameraSteps.zoom;
redrawCanvas();
}
}
ctx.drawText = function(text, x, y) {
this.save();
this.transform(1 / camera.zoom, 0, 0, -1 / camera.zoom, x, y);
this.fillText(text, 0, 0);
this.restore();
}
function redrawCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(canvas.width / 2 - (camera.x * camera.zoom),
canvas.height / 2 + (camera.y * camera.zoom));
ctx.scale(camera.zoom, -camera.zoom);
for (var i = 0; i < 10; i++) {
ctx.beginPath();
ctx.arc(5, i * 2, .5, 0, 2 * Math.PI);
ctx.drawText(i, 7, i*2-0.5);
ctx.fill();
}
ctx.restore();
}
</script>
</html>
编辑:根据 Blindman67 的建议修改文本缩放方法。还通过使相机运动渐进来改进演示。
关于javascript - "Undo"用于编写文本的 Canvas 转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41551931/
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]
这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test