【问题标题】:How can I use scrollbars to pan around a virtual canvas?如何使用滚动条在虚拟画布上平移?
【发布时间】:2011-11-01 08:28:43
【问题描述】:

我想创建一个实现虚拟 HTML5 画布的 jQuery 插件,即物理上不比页面外观大(或大不了多少)的画布。但是打算在画布上显示的内容可能比画布大很多倍,并且将根据滚动条动态重绘。

您会认为这是非常常见的功能,但到目前为止,我还无法找到使用 jQuery 插件或其他方式的示例。这与例如非常相似。 SlickGrid 用于 Div,但它与 Canvas 不同。我可以想到两种解决方案:

  1. 使用 jQuery UI Slider 将滚动条实现为完全独立的元素,并使用其事件来控制 Canvas 重绘。

  2. SlickGrid 为 Div 所做的一切。它似乎使 Div 比正在显示的内容稍大,并且连接到滚动事件以动态向 Div 添加/删除元素。但我看不出它如何修改滚动条以使其看起来好像 Div 中的内容比当前显示的内容多得多。

你会推荐什么?示例代码将不胜感激。

【问题讨论】:

  • @Shog9 这个问题怎么被删了?我可以在修订中看到取消删除事件但没有删除..
  • 符合the 30-day rule; jensk 联系了我们,它被救了,@Shadow
  • @Shog9 365 天规则,是的...但是修订版中没有“已被社区删除”,那么这是一个错误吗?我最近注意到,当帖子被 6 位审阅者从审阅队列中删除时,没有对此进行修订,也许是相关的?
  • 是的;当社区删除帖子时,它们会默默消失。这是众所周知的,并且可能会在某个时候修复;但是,请注意,在修订条目中将删除归因于社区将防止帖子在没有版主参与的情况下被取消删除 - 这可能并不理想,@Shadow。附:如果您需要更多详细信息,您应该将其提交给 MSO;在这里有点无关紧要。

标签: javascript jquery html canvas


【解决方案1】:

我深入研究了 SlickGrid 代码并使用了方法 2)(有点)——我想到的是这样的:

/*
    jensk@migselv.com

    Simple virtual CANVAS controlled by a native scrollbar made with two DIVs
    Uses jCanvas by Caleb Evans (http://calebevans.me/projects/jcanvas/index.php)
    Thanks to Michael Leibman of SlickGrid fame for ideas.

    Still need to clean it up (get rid of hardcoded values) and make it a nice, configurable
    jQuery component.

    Currently also redraws the entire canvas on each scroll event. Could be optimized to 
    do real browser scrolling and only redrawing the needed parts.

    Another gotcha is that since it is the zero width DIVs that causes the scroll events,
    mouse wheel, trackpad, touchscreen etc. scrolling over the Canvas will not work - only
    the scrollbar is active. To solve this, one could make the Canvas larger inside a 
    smaller DIV too, catch scroll events from it and perform redrawing and setting the DIV's
    scrollTop accordingly.    
*/

var h = 10000; // virtual canvas height
var vp = 400; // viewport height
var viewport, fakescrolldiv, canvas;

function onScroll() {
    var scrollTop = viewport.scrollTop();
    console.log("onScroll scrollTop=" + scrollTop);


    $("canvas").clearCanvas();

    // Red box top
    $("canvas").drawRect({
        fillStyle: "#F00",
        x: 150,
        y: 20 - scrollTop,
        width: 100,
        height: 100,
        fromCenter: false
    });

    // Green box middle
    $("canvas").drawRect({
        fillStyle: "#0F0",
        x: 150,
        y: 140 - scrollTop,
        width: 100,
        height: 100,
        fromCenter: false
    });

    // Blue box bottom
    $("canvas").drawRect({
        fillStyle: "#00F",
        x: 150,
        y: 260 - scrollTop,
        width: 100,
        height: 100,
        fromCenter: false
    });

    var i = 0;
    for (i = 0; i <= 396; i++) {
        $("canvas").drawLine({
            strokeStyle: "#000",
            strokeWidth: 1,
            x1: 0,
            y1: i,
            x2: (scrollTop + i) % 50,
            y2: i
        });
        if ((scrollTop + i) % 50 === 0) {
            $("canvas").drawText({
                fillStyle: "#729fcf",
                text: (scrollTop + i).toString(),
                align: "left",
                baseline: "top",
                font: "normal 12pt Verdana",
                x: 60,
                y: i
            });

        }
    }
}

$(function() {
    viewport = $("#viewport");
    fakescrolldiv = $("#fakescrolldiv");
    canvas = $("#gfx");

    viewport.css("height", vp);
    fakescrolldiv.css("height", h);

    viewport.scroll(onScroll);
    viewport.trigger("scroll");
});

Live demo

非常感谢任何关于改进或简化的建议。

【讨论】:

  • 比我聪明的人的练习:上面的解决方案不会对画布上的滚动事件做出反应(鼠标滚轮、触控板、移动设备上的触摸屏)。如何捕获这些事件并将其发送到 div 以修改滚动条,从而重新绘制画布?
  • 谢谢,正在寻找这样的东西。明智地使用代表! :)
【解决方案2】:

根据我的个人经验,您的选项 (1) 是一个很有吸引力的选择,但是将它变成一个可用的 jQuery 插件可能会有一些有趣的地方。

我一直致力于开发一个财务数据可视化系统,该系统明确使用 HTML5 Canvas 进行图形和图表绘制。我们在画布中有不同的虚拟“场景”或“幻灯片”,它们在画布中“滑入”和“滑出”,就像您在大型虚拟画布中导航的方式一样。所有的事件处理按钮都专门绘制在画布上,它决定了我们将显示哪个屏幕,但我们有一个/两个普通的 HTML 表单,它接受用户输入并带来这些“幻灯片”。我们使用 jQuery 来处理来自这些文本框的事件,但 jQuery 代码深深嵌套在其他 Canvas 绘图代码中(与外部调用不同,后者将是制作插件的理想候选者)。

滑动或更新画布是另一回事。这是因为它不仅取决于触发更新的 jQuery 事件,还取决于负责更新的 Canvas 框架(纯代码或 KineticJS、EaselJS、jCotton 等)。如果您使用框架,则还需要与框架交互。

为简单起见,我们假设有一个回调函数,您可以使用诸如移动偏移 (x, y) 之类的参数调用该 Canvas 框架,并且该框架会将这个偏移添加/删除到所有画布的 x 和 y 位置在画布中绘制的对象,大多数 Canvas 绘图框架还有一个 render() 函数,它会定期调用,以便下次绘制场景时自动显示结果(在您的情况下,滚动虚拟画布)。

因此,它基本上归结为不仅将其编写为 jQuery 插件,而且还将其绑定到特定的 Canvas 框架,例如KineticJS 或其他。

如果您使用基本的 Canvas 函数而不是使用这些框架中的任何一个,那就另当别论了,您可以为画布编写自己的渲染和更新函数,但在这种情况下,它将限制潜在用户遵守绘图功能的限制,除非您公开 API 来扩展它们;但是话又说回来,这意味着您正在编写自己的 Canvas 框架:)

我不确定我是否正确理解了您的问题,在这种情况下,您可以放心地忽略我的建议 :),但如果我是对的,我的意见是:制作这样的插件还需要绑定到Canvas 框架使其真正可用。

希望对你有帮助。

【讨论】:

  • 这样做的最初目的是制作一个既可以动态绘制(即实时添加事件)又具有非常大的历史记录的 UML 序列图 - 因此是滚动条。现在我不太确定是否要使用原始 HTML5 Canvas,但正在尝试使用 KineticJS。感谢您的评论和洞察力。
猜你喜欢
  • 2011-12-05
  • 2015-01-15
  • 2020-12-02
  • 1970-01-01
  • 1970-01-01
  • 2020-12-15
  • 1970-01-01
  • 2013-09-23
  • 1970-01-01
相关资源
最近更新 更多