65.9K
CodeProject 正在变化。 阅读更多。
Home

MVVM与HTML5/KnockoutJS/SVG

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (5投票s)

2012 年 8 月 11 日

CPOL

8分钟阅读

viewsIcon

50942

downloadIcon

969

HTML5/SVG MVVM与KnockoutJS

引言

本文旨在演示如何借助KnockoutJS库编写一个基于MVVM的HTML5/SVG应用。

SVG,即可缩放矢量图形,是一种基于XML的文件格式规范,能够以紧凑便携的形式表示二维图形信息。SVG规范是一个开放标准,自1999年以来一直由万维网联盟(W3C)开发。包括IE9及更高版本在内的所有现代浏览器都支持SVG。

模型-视图-视图模型(Model-View-ViewModel,MVVM)是软件工程中使用的一种架构模式,起源于微软,是Martin Fowler提出的表示层模型设计模式的特化。它是一种出色的设计模式,用于促进视图(表示/图形)与模型(数据/业务逻辑)之间清晰的关注点分离。

可以使用传统的JavaScript和HTML5编写出色的基于SVG的Web应用程序。然而,在没有MVVM模式的情况下编写传统应用程序的困境通常是,最终可能会得到一个将表示层与模型层纠缠在一起的单体代码。这会导致代码难以维护。

有一些出色的MVVM支持的开源库,其中一个出色的库是Steven Sanderson开发的KnockoutJS。简而言之,KnockoutJS是一个JavaScript库,它可以通过声明式数据绑定、自动UI刷新、依赖跟踪和模板化来帮助实现MVVM设计模式。

在接下来的部分,我将向您展示如何使用KnockoutJS编写一个简单的基于MVVM的HTML/SVG应用程序。我将称之为“儿童绘画”应用。

背景

对于MVVM模式的新手,我建议阅读一些关于MVVM模式的资料。一个好的起点是Josh Smith在MSDN上的一篇文章 - 《使用MVVM设计模式的应用》

我还建议那些对MVVM、MVC和MVP之间的区别感到好奇的人阅读Joel Wenzel的一篇精彩文章,该文章剖析了MVVM与MVP与MVC的区别

此外,我假设您在阅读本文之前,一定对SVG/HTML5感兴趣,因此我假定您至少具备一些基本的SVG工作知识。至少,您应该熟悉它的功能,以及如何绘制简单的基本图形,如矩形、圆形和线。市面上有大量的文章、书籍、教程和SVG库可供您参考。维基百科和W3School是一个很好的起点。如果您想了解SVG的功能,我最喜欢的JS SVG库之一是RaphaelJS。此外,O'Reilly出版社David Eisenberg的《SVG Essentials》是一本非常切实的优秀书籍。

您还应该做的最后一件事是查看KnockoutJS(也称为KO)并阅读其出色的文档,其中包含一些非常易于遵循的系统性示例。除非您具备KO的基本工作知识,否则很难理解我的文章。我的KidsDrawing应用大量使用了KO的<... data-bind="..." />属性以及诸如foreachif之类的模板化流程控制结构。因此,强烈建议您熟悉KO,以便能够理解本文中介绍和讨论的示例和概念。

我发现最初理解该库会有一个陡峭的学习曲线,但一旦您掌握了它,就会发现它极其强大且易于使用。我花了大约2天时间学习示例并理解概念。如果您是Silverlight的资深用户,我想您可能可以跳过所有这些,仍然能够很好地理解。我建议您查看http://learn.knockoutjs.com提供的出色的教程,以及John Papa关于KO/MVVM的精彩入门视频教程 - “使用MVVM和Knockout构建HTML5和JavaScript应用”。

儿童绘画应用

在我们开始编写代码之前,请允许我介绍一下我们即将编写的应用程序。儿童绘画是一款任何孩子(我猜想从2岁起)都可以用来绘制和着色矩形圆形三角形的应用程序。它是一个基于HTML/SVG的应用程序,可以在任何支持SVG的现代浏览器中运行。

PC版本的在线演示网址:KidsDrawing 在线演示。iPad版本:KidsDrawing iPad 在线演示

编写儿童绘画代码

与任何SVG HTML应用程序一样,您将从以下骨架开始。下面的代码片段将为您提供一个HTML页面,其中在屏幕位置x=100、y=100处绘制一个红色矩形,宽度=200;高度=200。

<html>
<head>
<title>Kids Drawing</title>
<body>
    <div>
        <svg xmlns="http://www.w3.org/2000/svg">
            <rect fill="red" x="100" y="100" width="200" height="200" />
        </svg>
    </div>
</body>
 

接下来,您需要设置KidsDrawing的绘制布局,它由左侧的调色板面板、右侧的形状面板以及中间的绘图面板组成。此外,还有一个用于标题的标题部分。

我们将通过首先创建我们要稍后使用的原始图形列表来做到这一点,方法是为调色板形状面板创建<svg> defs。

    <defs>
        <!-- defs for Palettes and shapes -->
        <rect id="square" width="100" height="50"></rect>
        <polygon id="triangle" points="50,0 100,50 0,50"></polygon>
        <circle id="circle" r="25"></circle>                

        <!-- defs for Drawing panel -->
        <rect id="drawingbox" width="500" height="500" fill="azure" stroke="lightgray" transform="translate(120,100)"></rect>
    </defs>

现在我们已经定义了原始图形,在下一个代码片段中,我们将使用这些原始图形来构建我们的调色板面板,我们将使用KO指令动态创建调色板面板。

为此,我们首先需要定义我们的视图模型,并将视图模型与SVG视图层通过KO进行绑定。其思想是,我们将在视图模型中定义我们的调色板列表,让KO发挥其魔力,负责视图层,通过绘制相应的SVG原始图形,并通过在视图模型中定义的observable对象创建绑定,以便在视图模型初始化和/或更改时,自动处理视图层的更改,而无需任何额外代码。

我应该提到,这就是KO和MVVM模式有助于简化事物的方式。在这种情况下,我们希望将调色板定义为observableArray

如果您是KO的新手,您可以查看此处此处了解observable是什么,它如何以及为什么有用。还要查看observableobservableArray之间的区别。一个是单个项目,另一个是项目集合(数组)。

<script type="text/javascript">
    var ViewModel = function () {
    this.palletes = ko.observableArray([
                { fill: "lightblue", y: 0 },
                { fill: "orange", y: 80 },
                { fill: "purple", y: 160 },
                { fill: "gray", y: 240 },
                { fill: "lightgreen", y: 320 },
                { fill: "red", y: 400 }
            ]);
    }

    ko.applyBindings(new ViewModel());
</script>>

接下来,在创建绑定之后,我们需要定义我们创建的视图并定位绑定。为此,KO提供了模板指令,例如foreach,我们可以通过它动态地迭代observable对象,并为数组中的每个条目重复一部分标记,并将该标记的每个副本绑定到相应的数组项。这是一个用于渲染列表或表格的便捷指令。

以下SVG代码片段绘制了绘图面板,通过迭代我们在ViewModel中创建的每个调色板来绘制调色板面板,并侦听ViewModel.palettes observableArray所做的任何更改。因此,如果我们向ViewModel.palettes添加/删除项,视图将通过我们之前创建的绑定自动更新。

这里有几点值得注意。第一点是如何绑定我们在ViewModel中创建的调色板对象的“fill”属性,使用data-bind指令。这意味着,在迭代每个调色板对象时,KO foreach将生成一个新的<use>标记,并将<use>的style:fill属性设置为我们正在迭代的调色板对象的fill值。此外,我们还将<use>的y坐标属性绑定到调色板对象的y值。这就是我们水平布局调色板面板的方式。此外,为了使调色板面板响应mousedown事件,我们将其绑定到一个接受“fill”值作为参数的ViewModel.changeColor()函数。请注意,这就是我们如何使调色板面板中的每个调色板按钮能够更改我们在绘图面板上绘制的形状的颜色。

(有关绑定鼠标事件的更多信息,请参见此处此处。它还应该能解答您对$root是什么以及$data意味着什么的疑问)。

<use xlink:href="#drawingbox"></use>

<!-- ko foreach:palletes -->
<g transform="translate(50, 100)">
    <use xlink:href="#circle" data-bind="style:{fill:fill},attr:{y:y},
         event:{mousedown:$root.changeColor.bind($data,fill)}"></use>
</g>
<!-- /ko -->

<!-- ko foreach:shapes -->
<g transform="translate(650,180)">
    <!-- ko if: $root.isRect(shape) -->
    <use xlink:href="#square" class="shape" transform="translate(0,0)" 
         data-bind="event:{mousedown:$root.changeShape.bind($data,shape)}"></use>
    <!-- /ko -->
    <!-- ko if: $root.isTriangle(shape) -->
    <use xlink:href="#triangle" class="shape" transform="translate(0,100)"
         data-bind="event:{mousedown:$root.changeShape.bind($data,shape)}"></use>
    <!-- /ko -->
    <!-- ko if: $root.isCircle(shape) -->
    <use xlink:href="#circle" class="shape" transform="translate(50,250)"
         data-bind="event:{mousedown:$root.changeShape.bind($data,shape)}"></use>
    <!-- /ko -->
</g>
<!-- /ko -->

现在您已经明白了,一旦您将这些东西与少量额外的代码结合起来进行粘合 - 瞧!!!您就有了一个交互式的基于MVVM的SVG/HTML5应用程序KidsDrawing,它非常具有交互性,而且代码量很少。这一切都归功于KnockoutJS为您做了很多工作,提供了MVVM构建块(数据绑定和模板化),使您能够通过ModelView视图模型分离。

您可以通过以下链接查看在线演示并检查完整的代码:PC版本和iPad版本。

摘要

总而言之,SVG对于计算机图形爱好者来说是一份礼物(对我来说确实如此)。它非常易于使用,同时又足够强大,可以完成复杂的事情。它们使它如此简单易用,以至于几乎任何懂得HTML和XML的人突然都知道如何在浏览器中进行丰富的图形操作。此外,它运行在所有现代浏览器上,这一点非常棒。

KnockoutJS是一个出色的数据绑定和模板化库,可以帮助人们在其HTML5应用程序中实现MVVM设计模式。没有KnockoutJS,在HTML5/JavaScript上编写基于MVVM的应用程序将需要额外的努力。它有出色的文档和一套强大的功能(例如,模板化、数据绑定、自动UI刷新等),可以帮助任何人编写强大的应用程序。

MVVM是一个优秀的设计模式,它通过ViewModel实现视图和模型之间的良好关注点分离,并使用数据绑定、模板、命令作为构建块。构建正确的构建块需要额外的努力,但一旦拥有了构建块(例如KO提供的),编写干净、健壮且易于维护的代码就非常容易了。

历史

  • V1.0:一个带有6种颜色调色板和3种可绘制形状的KidsDrawing应用程序。
© . All rights reserved.