使用Backbone和EaselJS实现拖放

在本文中,我们将使用EaselJSBackbone.js构建一个简单的拖放应用程序。 骨干网将通过提供模型集合视图来为我们的应用程序提供结构。 画架将使使用HTML5 canvas元素变得容易。 尽管对于这样一个简单的应用程序,我们不一定需要Backbone,但是以这种方式开始使用Backbone很有趣。

开始使用

首先,我们如下创建目录结构:

.
|-- index.html
+-- js
    |-- main.js
    |-- models
    |   +-- stone.js
    +-- views
        +-- view.js

接下来,在index.html包括JavaScript文件和canvas元素,如以下代码示例所示。 完成此操作后,我们就可以操纵canvas

<body>
  <!-- Canvas Element -->
  <canvas id="testcanvas" height="640" width="480"/>

  <script src="/bower_components/jquery/jquery.min.js"></script>
  <!-- underscore is needed by backbone.js -->
  <script src="/bower_components/underscore/underscore-min.js"></script>
  <script src="/bower_components/backbone/backbone.js"></script>
  <script src="/bower_components/easeljs/lib/easeljs-0.7.1.min.js"></script>
  <!-- tweenjs is for some animations -->
  <script src="/bower_components/createjs-tweenjs/lib/tweenjs-0.5.1.min.js"></script>
  <script src="/js/models/stone.js"></script>
  <script src="/js/views/view.js"></script>
  <script src="/js/main.js"></script>
</body>

骨干模型

通过创建Backbone模型,我们将在该模型上具有键值绑定和自定义事件。 这意味着我们可以听取模型属性的更改并相应地渲染视图。 骨干网集合是一组有序的模型。 您可以绑定change事件,以便在集合中的任何模型发生更改时得到通知。 接下来,让我们创建一个石头模型和一个石头收藏。 以下代码属于js/models/stone.js

var Stone = Backbone.Model.extend({

});

var StoneCollection = Backbone.Collection.extend({
  model: Stone
});

使用EaselJS初始化主干视图

骨干视图无法确定任何有关HTML的内容,并且可以与任何JavaScript模板库一起使用。 在我们的例子中,我们没有使用模板库。 相反,我们操纵canvas 。 您可以将视图的render()函数绑定到模型的change事件,以便在模型数据更改时,视图会自动更新。

为了开始使用画架,我们创建了一个包装canvas元素的阶段,并将对象添加为子对象。 稍后,我们将这一阶段传递给我们的骨干观点。 js/main.js中实现此目的的代码如下所示。

$(document).ready(function() {
  var stage = new createjs.Stage("testcanvas");
  var view = new CanvasView({stage: stage}).render();
});

我们已经创建了CanvasView并调用了它的render()函数来渲染它。 我们将很快回顾render()的实现。 首先,让我们看一下我们的initialize()函数,该函数在js/views/view.js定义。

var CanvasView = Backbone.View.extend({
  initialize: function(args) {
    // easeljs stage passed as argument.
    this.stage = args.stage;
    // enableMouseOver is necessary to enable mouseover event http://www.createjs.com/Docs/EaselJS/classes/DisplayObject.html#event_mouseover
    this.stage.enableMouseOver(20);

    // stone collection
    this.collection = new StoneCollection();

    // bounds of pink area and our stones. the pink area is called "rake".
    this.rakeOffsets = {
      x: 10,
      y: 400,
      height: 150,
      width: 300,
      stoneWidth: 50,
      stoneHeight: 50
    };

    // listen to collection's add remove and reset events and call the according function to reflect changes.
    this.listenTo(this.collection, "add", this.renderStone, this);
    this.listenTo(this.collection, "remove", this.renderRake, this);
    this.listenTo(this.collection, "reset", this.renderRake, this);
  },
  //...
});

listenTo()侦听模型/集合更改,并调用作为第二个参数传递的函数。 我们传递上下文,该函数作为第三个参数被调用。 当我们向集合中添加石头时, add事件将调度this.renderStone()并将新的石头传递给函数。 同样,当重置集合时, reset事件将调度this.renderRake() 。 通过实现这些渲染功能,视图将始终与集合同步。

渲染视图

如下所示的render()函数仅调用this.renderRake()并更新舞台。

render: function() {
  this.renderRake();

  // stage.update is needed to render the display to the canvas.
  // if we don't call this nothing will be seen.
  this.stage.update();

  // The Ticker provides a centralized tick at a set interval.
  // we set the fps for a smoother animation.
  createjs.Ticker.addEventListener("tick", this.stage);
  createjs.Ticker.setInterval(25);
  createjs.Ticker.setFPS(60);
},

下面显示了renderRake()方法,该方法也存储在js/views/view.js中。

renderRake: function() {
  // http://stackoverflow.com/questions/4886632/what-does-var-that-this-mean-in-javascript
  var that = this;

  // create the rake shape
  var rakeShape = new createjs.Shape();

  rakeShape.graphics.beginStroke("#000").beginFill("#daa").drawRect(this.rakeOffsets.x, this.rakeOffsets.y, this.rakeOffsets.width, this.rakeOffsets.height);

  // assign a click handler
  rakeShape.on("click", function(evt) {
    // When rake is clicked a new stone is added to the collection.
    // Note that we add a stone to our collection, and expect view to reflect that.
    that.collection.add(new Stone());
  });

  // add the shape to the stage
  this.stage.addChild(rakeShape);

  // a createjs container to hold all the stones.
  // we hold all the stones in a compound display so we can
  // easily change their z-index inside the container,
  // without messing with other display objects.
  this.stoneContainer = new createjs.Container();
  this.stage.addChild(this.stoneContainer);

  // for each stone in our collection, render it.
  this.collection.each(function(item) {
    this.renderStone(item);
  }, this);
},

renderRake()做两件事。 首先,它在画布上渲染耙形(粉红色矩形),并创建一个click
处理程序。 其次,它遍历stone集合并在每个项目上调用renderStone()click处理程序将新的石头添加到集合中。

接下来,让我们看一下renderStone()函数。

renderStone: function(model) {
  // var that = this;
  var baseView = this;

  // build the stone shape
  var stoneShape = buildStoneShape();

  // make it draggable
  // the second argument is a callback called on drop
  // we snap the target stone to the rake.
  buildDraggable(stoneShape, function(target, x, y) {
    rakeSnap(target, false);
  });

  // add the stone to the stage and update
  this.stoneContainer.addChild(stoneShape);
  this.stage.update();

  function buildStoneShape() {
    var shape = new createjs.Shape();

    shape.graphics.beginStroke("#000").beginFill("#ddd").drawRect(0, 0, baseView.rakeOffsets.stoneWidth, baseView.rakeOffsets.stoneHeight);
    return shape;
  };
},

我们调用了buildDraggable()函数来使石头可拖动。 接下来,我们将看到如何实现。 但是首先,让我们回顾一下我们的骨干视图是如何工作的。 CanvasView侦听集合的add事件,并在add新石头时调用renderStone()render()方法渲染耙并在集合中的每块石renderStone()调用renderStone() 。 单击耙时,将新的石头模型添加到石头集合中,然后在新石头上调用renderStone()

现在,让我们看一下实现拖放功能的buildDraggable()函数:

renderStone: function(model) {
  // ...

  function buildDraggable(s, end) {
    // on mouse over, change the cursor to pointer
    s.on("mouseover", function(evt) {
      evt.target.cursor = "pointer";
    });

    // on mouse down
    s.on("mousedown", function(evt) {
      // move the stone to the top
      baseView.stoneContainer.setChildIndex(evt.target, baseView.stoneContainer.getNumChildren() - 1);

      // save the clicked position
      evt.target.ox = evt.target.x - evt.stageX;
      evt.target.oy = evt.target.y - evt.stageY;

      // update the stage
      baseView.stage.update();
    });

    // on mouse pressed moving (drag)
    s.on("pressmove", function(evt) {
      // set the x and y properties of the stone and update
      evt.target.x = evt.target.ox + evt.stageX;
      evt.target.y = evt.target.oy + evt.stageY;
      baseView.stage.update();
    });

    // on mouse released call the end callback if there is one.
    s.on("pressup", function(evt) {
      if (end) {
        end(evt.target, evt.stageX + evt.target.ox, evt.stageY + evt.target.oy);
      }
    });
  };
  // ...
},

为了将石头固定在耙子上,这是我们需要的最终实用函数。

// drag the stone, either by animating or not
function dragStone(s, x, y, animate) {
  if (animate) {
    // Use tween js for animation.
    createjs.Tween.get(s).to({x: x, y: y}, 100, createjs.Ease.linear);
  } else {
    // set x and y attributes without animation
    s.x = x;
    s.y = y;
  }

  // update
  baseView.stage.update();
};

// calculate x position to snap the rake
function snapX(x) {
  if (x &lt; baseView.rakeOffsets.x) {
    x = baseView.rakeOffsets.x;
  } else if (x > baseView.rakeOffsets.x + baseView.rakeOffsets.width - baseView.rakeOffsets.stoneWidth) {
    x = baseView.rakeOffsets.x + baseView.rakeOffsets.width - baseView.rakeOffsets.stoneWidth;
  }

  return x;
};

// calculate y position to snap the rake
function snapY(y) {
  if (y &lt; baseView.rakeOffsets.y) {
    y = baseView.rakeOffsets.y;
  } else if (y > baseView.rakeOffsets.y + baseView.rakeOffsets.height - baseView.rakeOffsets.stoneHeight) {
    y = baseView.rakeOffsets.y + baseView.rakeOffsets.height - baseView.rakeOffsets.stoneHeight;
  }

  return y;
};

// drag stone within the rake bounds. animation is disabled if second argument is given. animation is enabled by default
function rakeSnap(s, animateDisabled) {
  dragStone(s, snapX(s.x), snapY(s.y), !animateDisabled);
};

结论

总之,Backbone不限于DOM操作,可以在需要模型视图结构的任何地方使用。 尽管它可以用于构建单页应用程序,但它不是一个完整的框架,在本文中我们仅看到Backbone的一侧。 如果您想将Backbone用于大型应用程序,我建议使用Marionette.js ,它可以处理Backbone的一些原始问题。

这篇文章的完整代码可以在GitHub找到Heroku上还提供了现场演示。 首先,只需单击粉红色区域即可创建可拖动的石头。 石头将是可拖动的,并且将被限制在粉红色区域内。

From: https://www.sitepoint.com/implementing-drag-drop-using-backbone-easeljs/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值