fabric.js 调整画布大小以适应屏幕

10
我目前正在使用ionic和cordova在移动设备(Android和iPhone)上开发应用程序。我想编辑一张图片,为此我使用了fabric.js库。Fabric.js将图像和其他项目转换为画布,然后我在画布上添加一些图片(贴纸),并将其导出为图像(dataURL)。因此,画布的大小非常重要,因为它是一个质量因素(基于小画布导出图像将导致质量差)。画布大小约为607*1080像素(取决于拍摄照片的设备)。我想将其适应屏幕而不失去质量(就像下面解释的那样,我无法调整画布大小而不失去质量)。
我尝试了三种方法但都没有成功。 1. 调整画布大小:会导致图片质量下降(导出图像将具有画布大小) 2. 适应视口:cordova不允许在移动设备上更改视口。有一个解决方法,但这会破坏我的设计(非常小的导航栏,较小的菜单等) 3. 使用CSS3缩放属性:在桌面上运行得很好,但在移动设备上存在事件映射问题(我没有在ios上尝试过,但在android WebView上遇到了问题)。事件映射没有被缩放。画布被缩小,但无法与画布元素进行交互。如果一个画布元素(贴纸)位于x:100 y:100处,当我将缩放属性设置为0.5时,该元素将位于(50,50),而不再是(100,100)。是否有任何方法根据android webview的缩放属性来纠正事件映射?
我不知道是否有其他方法可以解决这个问题,但我已经被困扰了三天,并且在论坛、fabricjs文档和谷歌上花费了很多时间,但没有找到任何可行的解决方案或其他可以帮助我的东西。 编辑:下面有两种可行的解决方法。检查我的答案以了解如何使用CSS完成。

我以前遇到过类似的问题。我认为你应该将fabric.js的计算复制到事件计算中...... - Alejandro Teixeira Muñoz
1
很多时候,自己编写代码比试图使用打包的库更好。 - Alejandro Teixeira Muñoz
我没有时间编写一个实现fabris.js中包含的所有功能的库。我使用了许多这个库的功能,如果从头开始重新创建一切,将会花费太多时间。 - X4V1
1
你可能会编写另一个库,然后决定与人分享。然后会有人对那个库提出问题。 - AndreaBogazzi
2个回答

14

1. 使用CSS为画布设置正确的分辨率:

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d");

canvas.width = 800;
canvas.height = 600;

层叠样式表:

/* media query 1 */
    #canvas{
       width: 400px;
       height: 250px; 
    }

/* media query 2 */
#canvas{
       width: 200px;
       height: 120px; 
    }

优点:

所有的分辨率都由画布宽度和高度(800/600)给出。

缺点:

适用于正方形画布比例(1:1),对于需要基于 CSS 高度计算的奇怪宽高比输出存在问题。

original height / original width * new width = new height;

在CSS方面,这超出了我的能力范围...

我曾经在fabric.js的git问题部分阅读到,由于速度缓慢和对象事务不正确,技术(css resize)并不推荐使用,我知道这可能会改变,并且我测试过,对象被重新调整大小时不正确(随机着色像素,事务阴影仍然停留在画布中,换句话说就是一团糟)。

2. 每次需要提交时将画布调整为精确尺寸(分辨率),然后生成图像

只需在窗口大小调整(响应式画布)时重新调整画布大小,并且当您需要导出到精确尺寸时,您需要将画布调整为精确像素分辨率,或者创建另一个隐藏的画布。

此示例适用于1:1比例画布(您需要将画布和内部对象应用纵横比):

$(window).resize(function (){
 if (canvas.width != $(".container").width()) {
            var scaleMultiplier = $(".container").width() / canvas.width;
            var objects = canvas.getObjects();
            for (var i in objects) {
                objects[i].scaleX = objects[i].scaleX * scaleMultiplier;
                objects[i].scaleY = objects[i].scaleY * scaleMultiplier;
                objects[i].left = objects[i].left * scaleMultiplier;
                objects[i].top = objects[i].top * scaleMultiplier;
                objects[i].setCoords();
            }

            canvas.setWidth(canvas.getWidth() * scaleMultiplier);
            canvas.setHeight(canvas.getHeight() * scaleMultiplier);
            canvas.renderAll();
            canvas.calcOffset();
        }
});

在提交时导出画布数据并将画布大小调整为所需分辨率,完成:

function GetCanvasAtResoution(newWidth)
  {
    if (canvas.width != newWidth) {
                var scaleMultiplier = newWidth / canvas.width;
                var objects = canvas.getObjects();
                for (var i in objects) {
                    objects[i].scaleX = objects[i].scaleX * scaleMultiplier;
                    objects[i].scaleY = objects[i].scaleY * scaleMultiplier;
                    objects[i].left = objects[i].left * scaleMultiplier;
                    objects[i].top = objects[i].top * scaleMultiplier;
                    objects[i].setCoords();
                }

                canvas.setWidth(canvas.getWidth() * scaleMultiplier);
                canvas.setHeight(canvas.getHeight() * scaleMultiplier);
                canvas.renderAll();
                canvas.calcOffset();
            }
     return canvas.toDataURL();
   }
   );

如果比例不是1:1,计算高度的比例倍数也很容易,这并不是什么大问题。这里有一个易于理解比例的链接:http://andrew.hedges.name/experiments/aspect_ratio/


感谢您的帮助。CSS会裁剪图片,我的意思是如果我为画布设置宽度和高度,CSS将裁剪画布。但第二种方法似乎是一个好的解决方案。我还发现了另一种不需要复制画布就能完成的技巧。我也会在这里解释一下,但非常感谢您的帮助。 - X4V1

6
我找到了一个技巧,可以在不复制画布的情况下实现这一点。这个解决方案与css缩放属性非常接近,但事件处理得到了正确的处理。
以下是将画布显示为其一半大小的示例:
-webkit-transform : scale(0.5);
-webkit-transform-origin : 0 0;

这太聪明了,而且非常有效。我使用了这个 JS 技巧来缩放画布容器的比例。 - benbyford
这个技巧看起来很不错,但问题是当你添加对象时,它们的控件也会被放大太多。 - bukso

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接