【问题标题】:Html5 Complex shape and events - recommend an approach for maintainable and flexible codeHtml5 复杂形状和事件 - 推荐一种可维护和灵活代码的方法
【发布时间】:2012-01-24 13:42:55
【问题描述】:

我需要创建一个 HTML5 画布来捕获一些输入。 我想渲染一个线条图(白底黑线)并允许用户为这些部分着色。

然后我希望能够在最后检索此数据以在服务器端对其进行处理,以便以后可以再次呈现。

我想将用于渲染图像的数据存储在数据库中,以便可以绘制任何图像。

我研究了 HTML5 和 canvas 标签。也在 Kinetic js 库中。我已经能够快速演示我需要的内容。

http://jsfiddle.net/6qskx/

从我演示的情况来看,这种方法很难维护,也很难首先设置图像,因为每个部分都需要制定和绘制,以便可以将事件添加到其中。

我的问题是 - 有没有人有任何类似这样的经验或任何建议的许多框架:

  • 哪个框架最好
  • 哪种方法最灵活且可维护。

【问题讨论】:

  • 不同部分是静态的还是动态的?
  • 嗯 - 对用户来说是静态的,对管理员来说是动态的。我的意思是,一旦用户使用该网站,并且据 html 所知,这些部分将是静态的。部分的坐标/点/路径将从数据库中动态加载。以便它可以与其他图像/形状一起扩展。
  • 假设您只使用线条图,您可能希望从数据库中加载它们。下班回家后我会玩弄它,不过没什么难的。
  • 你能用 SVG 代替画布吗? svg 用于通过 javascript 事件进行操作,保存到 db 非常简单,因为它基本上只是呈现 xml。

标签: javascript html html5-canvas fabricjs


【解决方案1】:

我在使用 Kinectic Js 库时遇到了类似的加载和检索数据(画布状态)的情况。

现在我正在使用织物 js 库。 https://github.com/kangax/fabric.js/

它通过canvas.toJSON()方法保存对象的属性或状态,然后用canvas.loadFromJSON() 方法。

【讨论】:

  • 哦不!好吧,KineticJS 很快就会支持舞台、层、组和形状的序列化。敬请期待!
【解决方案2】:

我玩过它,这个例子非常基础,不适合生产。 您可以在http://xisse.net/shapes.php 查看示例 并在http://xisse.net/shapes.zip下载源代码 您将需要一个运行 php 的网络服务器和一个 mysql 数据库。 .zip 文件中包含一个 sql 文件。 只需在 shape.php 文件中编辑 db 连接即可。 我还将在下面发布代码:

shapes.php

    <html>
<head>
    <?php
    //connect to db
    mysql_connect('localhost','user','password');
    mysql_select_db('shapes');
    ?>
    <style>
       canvas
        {
            border: 1px solid #9C9898;
        }
    </style>

    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
    <script src="http://www.html5canvastutorials.com/libraries/kinetic-v3.1.0.js"></script>
    <script type="text/javascript" src="shapes.js"></script>
    <script type="text/javascript">


        $(document).ready(function () {
            var stage;
            var canvas;
            var context;
            var cursor;
            var item = [];
            var rectX;
            var rectY

            prepareCanvas();

            function prepareCanvas() {

                stage = new Kinetic.Stage("container", 578, 200);
                canvas = stage.getCanvas();

                rectX = canvas.width / 2 - 50;
                rectY = canvas.height / 2 - 25;

                cursor = setupCursor();

                setupColours();

                setupItem();

                $('input[name=type]').change(setupItem);
            }

            /* Functions */

            function setupCursor() {
                return new Kinetic.Shape(function () {
                    var context = this.getContext();
                    context.beginPath();
                    context.lineWidth = 1;
                    context.strokeStyle = "black";
                    context.fillStyle = cursor.color;
                    context.arc(0, 0, 10, 0, Math.PI * 2, true);

                    context.closePath();
                    context.fill();
                    context.stroke();
                });
            }

            function setupColours() {
                var cBlack = createColour(20, 20, 20, 'black');
                var cWhite = createColour(20, 50, 20, 'white');
                var cRed = createColour(20, 20, 50, 'red');
                var cBlue = createColour(20, 50, 50, 'blue');
            }

            function createColour(size, x, y, colour) {
                var rectangle = new Kinetic.Shape(function () {
                    var context = this.getContext();
                    context.beginPath();
                    context.lineWidth = 1;
                    context.strokeStyle = 'black';
                    context.fillStyle = colour;
                    context.moveTo(x, y);
                    context.lineTo(x, y + size);
                    context.lineTo(x + size, y + size);
                    context.lineTo(x + size, y);
                    context.closePath();
                    context.fill();
                    context.stroke();
                });

                rectangle.addEventListener("click", function () {

                    cursor.color = colour;

                    addCursor();
                });

                stage.add(rectangle);

                return rectangle;
            }

            function moveCursor() {
                var mousePos = stage.getMousePos();

                cursor.x = mousePos.x + 10;
                cursor.y = mousePos.y + 10;
                stage.draw();
            }

            function addCursor() {

                stage.add(cursor);
                moveCursor();
                stage.addEventListener("mousemove", moveCursor, false);
            }

            function setupItem() {

                if (item) {
                    if (item.length > 0) {
                        for (var n = 0; n < item.length; n++) {
                            stage.remove(item[n]);
                        }
                    }
                }

                /* set shape attributes */
                <?php
                    //get shape info
                    $qShapeDetails  =   "SELECT * FROM shapeDefinitions ORDER BY shapeID ASC";
                    $rShapeDetails  =   mysql_query($qShapeDetails) or die('mysql has thrown an error: \n' . mysql_error());
                    while($shape    =   mysql_fetch_assoc($rShapeDetails))
                {
                ?>
                var shape<?php echo $shape['shapeID']; ?> = new Kinetic.Shape(function () {
                    var context = this.getContext();
                    context.beginPath();
                    context.lineWidth = <?php echo $shape['lineWidth']; ?>;
                    context.strokeStyle = '<?php echo $shape['strokeColour']; ?>';
                    context.fillStyle = shape<?php echo $shape['shapeID']; ?>.color;
                    <?php
                        $qLines =   "SELECT * FROM linePos WHERE shapeID = '" . $shape['shapeID'] . "' ORDER BY lineID ASC";
                        $rLines =   mysql_query($qLines) or die('mysql has thrown an error: \n' . mysql_error());
                        while($line =   mysql_fetch_assoc($rLines))
                        {
                            if($line['lineID']  ==  1)
                            {
                    ?>
                                context.moveTo(<?php echo $line['posX']; ?>, <?php echo $line['posY']; ?>);
                    <?php
                            } else {
                    ?>  
                        context.lineTo(<?php echo $line['posX']; ?>, <?php echo $line['posY']; ?>);
                    <?php
                            }
                        }
                    ?>
                    context.closePath();
                    context.fill();
                    context.stroke();
                });

                shape<?php echo $shape['shapeID']; ?>.color = 'white';

                shape<?php echo $shape['shapeID']; ?>.addEventListener("click", function () {
                    shape<?php echo $shape['shapeID']; ?>.color = cursor.color;
                    stage.draw();
                });

                item.push(shape<?php echo $shape['shapeID']; ?>);
                <?php
                }
                ?>


                for (var n = 0; n < item.length; n++) {
                    stage.add(item[n]);
                }

                stage.draw();
            }
        });</script>
</head>
<body onmousedown="return false;">
    <div id="container" style="cursor:crosshair;">
    </div>
</body>
</html>

SQL

    SET FOREIGN_KEY_CHECKS=0;
    -- ----------------------------
    -- Table structure for `linepos`
    -- ----------------------------
    DROP TABLE IF EXISTS `linepos`;
    CREATE TABLE `linepos` (
      `lineID` int(11) NOT NULL AUTO_INCREMENT,
      `shapeID` int(11) DEFAULT NULL,
      `posX` float DEFAULT NULL,
      `posY` float DEFAULT NULL,
      PRIMARY KEY (`lineID`)
    ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;

    -- ----------------------------
    -- Records of linepos
    -- ----------------------------
    INSERT INTO `linepos` VALUES ('1', '1', '100', '10');
    INSERT INTO `linepos` VALUES ('2', '1', '100', '30');
    INSERT INTO `linepos` VALUES ('3', '1', '200', '30');
    INSERT INTO `linepos` VALUES ('4', '1', '200', '10');
    INSERT INTO `linepos` VALUES ('5', '2', '100', '30');
    INSERT INTO `linepos` VALUES ('6', '2', '100', '60');
    INSERT INTO `linepos` VALUES ('7', '2', '200', '60');
    INSERT INTO `linepos` VALUES ('8', '2', '200', '30');
    INSERT INTO `linepos` VALUES ('9', '3', '200', '20');
    INSERT INTO `linepos` VALUES ('10', '3', '200', '60');
    INSERT INTO `linepos` VALUES ('11', '3', '400', '60');
    INSERT INTO `linepos` VALUES ('12', '3', '400', '20');

    -- ----------------------------
    -- Table structure for `shapedefinitions`
    -- ----------------------------
    DROP TABLE IF EXISTS `shapedefinitions`;
    CREATE TABLE `shapedefinitions` (
      `shapeID` int(11) NOT NULL AUTO_INCREMENT,
      `lineWidth` float DEFAULT NULL,
      `strokeColour` varchar(255) DEFAULT NULL,
      `backGroundColour` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`shapeID`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;

    -- ----------------------------
    -- Records of shapedefinitions
    -- ----------------------------
    INSERT INTO `shapedefinitions` VALUES ('1', '2', '#000', '#FFF');
    INSERT INTO `shapedefinitions` VALUES ('2', '2', '#000', '#FFF');
    INSERT INTO `shapedefinitions` VALUES ('3', '2', '#000', '#FFF');

这应该会让你上路:)

【讨论】:

  • 对不起,我有一个数据库,不需要这部分的帮助。我对使用 HTML5 Canvas 的良好框架更感兴趣。我觉得我在概念证明中编写的代码是不可维护且不灵活的。我想知道其他人是如何解决这个问题的。
【解决方案3】:

在某些浏览器尤其是移动浏览器上无法查看 Svg,但 Raphael Js 图片可以。使用 Raphael,它是一个非常好的库,在 svg 中使用它们的原始路径绘制图像,在 Raphael 绘图函数中添加该数据,瞧,你的图像在画布中。 我使用 flex 和 fxg 以及矢量图像,当您需要构建界面时,没有什么比 fxg 和 flash 更快了,但是现在我们仅限于 html5 和讨厌的 javascript :) 就像我们回到 90 年代一样

【讨论】:

    【解决方案4】:

    我很惊讶没有人提到 raphael,因为它似乎是 javascript/canvas 交互的事实标准(至少它是我工作的地方)。

    它有一个简单的语法(使用 SVG 路径字符串)用于绘制/存储线/多边形http://raphaeljs.com/reference.html#Paper.path,用于为它们着色http://raphaeljs.com/reference.html#Element.attr 和用于处理事件http://raphaeljs.com/reference.html#Element.click。鉴于此,可以将您的绘图存储为逗号分隔的 SVG 路径列表,并将您的颜色存储为逗号分隔的值列表(在查看/着色图像时放入数组中),每个值对应于你的路径之一'

    rapahel 文档有点令人生畏,因为 api 最近大幅增长,没有也没有太多指示在哪里寻找什么(即使您非常了解旧 API),但主页上的演示对于了解 API 非常有用。

    【讨论】:

    • RaphaelJS 使用 SVG 而不是画布。
    【解决方案5】:

    我同意 wheresrhys。 SVG 似乎是您想要的更好的方法。在现有的 SVG 库中,raphael 允许您做很多事情。

    【讨论】:

      猜你喜欢
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 2014-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-11
      • 2013-03-06
      相关资源
      最近更新 更多