diff --git a/src/js/modules/components/moveable.js b/src/js/modules/components/moveable.js index 2fd3c38..3d79ac5 100644 --- a/src/js/modules/components/moveable.js +++ b/src/js/modules/components/moveable.js @@ -1,15 +1,26 @@ export const MOVE_END = 'moveend'; const PREVENT_SELECT_CLASS = 'noselect'; -export function moveableComponent({ view }) { +export function moveableComponent({ view, staticMode = false }) { const offset = {}; - const position = {}; + const position = { x: 0, y: 0 }; + + view.el.classList.add(PREVENT_SELECT_CLASS); + + function moveTo({ x, y }) { + position.x = x; + position.y = y; + view.el.style.transform = `translate3d(${x}px, ${y}px, 0)`; + } + + function getPosition() { + return position; + } function onMouseMove({ clientX, clientY }) { - position.x = clientX - offset.x; - position.y = clientY - offset.y; - view.el.style.transform = - `translate3d(${position.x}px, ${position.y}px, 0)`; + moveTo({ + x: clientX - offset.x, + y: clientY - offset.y }); } function onMouseUp() { @@ -26,6 +37,9 @@ export function moveableComponent({ view }) { document.addEventListener('mouseup', onMouseUp, false); } - view.delegate('mousedown', null, onMouseDown); - view.el.classList.add(PREVENT_SELECT_CLASS); + if (!staticMode) { + view.delegate('mousedown', null, onMouseDown); + } + + return Object.freeze({ moveTo, getPosition }); } diff --git a/src/js/modules/plant/model.js b/src/js/modules/plant/model.js index 277fc57..21f36dd 100644 --- a/src/js/modules/plant/model.js +++ b/src/js/modules/plant/model.js @@ -1,4 +1,5 @@ import { Model } from '../../core'; +import { Model as BModel } from 'backbone'; export default Model.extend({ defaults: { @@ -11,18 +12,50 @@ export default Model.extend({ height: 0, userActivity: false, }, + container: undefined, - getProjection: function() { + constructor({ containerWidth, containerHeight, ...props }, options) { + this.container = new BModel({ + width: containerWidth, + height: containerHeight }); + + return Model.call(this, props, options); + }, + + getProjection() { return this.manifesto() .getProjectionsFor(this.get('objectId'))[ this.get('currentProjection') || this.get('projection') || 0 ]; }, - setProjection: function(at_) { + setProjection(at_) { const projections = this.get('projections'); let at = at_ > projections.length ? 0 : at_; at = at < 0 ? projections.length : at; this.set('projection', at); }, + + setContainerSize({ width, height }) { + this.container.set({ width, height }); + }, + + setPosition({ x, y }) { + const { width, height } = this.container.attributes; + const props = { + x: x / width, + y: (y - (height / 2)) / width, + }; + + this.set(props); + }, + + getPosition() { + const { width, height } = this.container.attributes; + + return { + x: width * this.get('x'), + y: height / 2 + this.get('y') * width, + }; + }, }); diff --git a/src/js/modules/plant/object.js b/src/js/modules/plant/object.js index b4369cd..4fde28f 100644 --- a/src/js/modules/plant/object.js +++ b/src/js/modules/plant/object.js @@ -11,10 +11,14 @@ export default View.extend({ 'mouseover': 'setUserActivity', 'mouseleave': 'unsetUserActivity', }, - $img: null, + moveable: undefined, + $img: undefined, initialize: function(options) { + const isViewerMode = this.app.getState() === Const.State.VIEWER; + this.overlay = options.overlay; + this.moveable = moveableComponent({ view: this, staticMode: isViewerMode }); this.listenTo(this.model, 'change:scale', this.resize); this.render(); this.$img @@ -36,26 +40,21 @@ export default View.extend({ .on('change:currentProjection', this.updateProjection, this) .on('change:layerIndex', this.setLayer, this); - if (this.app.getState() !== Const.State.VIEWER) { - moveableComponent({ view: this }); - this.on(MOVE_END, this.model.set, this.model); + if (!isViewerMode) { + this.on(MOVE_END, this.model.setPosition, this.model); } }, render: function() { - const x = this.overlay.width() * this.model.get('x'); - const y = this.overlay.height() / 2 + this.model.get('y') * this.overlay.width(); + const { x, y } = this.model.getPosition(); this.$el .html(this.template({ projectionUrl: this.model.getProjection(), })) .attr('data-cid', this.model.cid) - .css({ - zIndex: this.model.get('layerIndex'), - transform: `translate3d(${x}px, ${y}px, 0)`, - }); - + .css('zIndex', this.model.get('layerIndex')); + this.moveable.moveTo({ x, y }); this.$img = this.$el.children('img'); return this; diff --git a/src/js/modules/plant/overlay.js b/src/js/modules/plant/overlay.js index f239414..6c20dcd 100644 --- a/src/js/modules/plant/overlay.js +++ b/src/js/modules/plant/overlay.js @@ -38,6 +38,7 @@ export default View.extend({ }, addObject: function(model) { + model.setContainerSize({ width: this._width, height: this._height }); const newObject = new PlantViewObject({ model: model, app: this.app, @@ -69,17 +70,15 @@ export default View.extend({ const $img = $container.children('img'); const height = $img.height(); const width = $img.width(); - const pos = $container.position(); - let top = pos.top; - const left = pos.left * scale; + const pos = object.moveable.getPosition(); + let top = pos.y; + const left = pos.x * scale; top = newH / 2 + (top - oldH / 2) * scale; $img.height(height * scale); $img.width(width * scale); - $container.css({ - top: top, - left: left, - }); + object.model.setContainerSize({ width: newW, height: newH }); + object.moveable.moveTo({ x: left, y: top}); }); this._width = newW; this._height = newH; @@ -90,6 +89,8 @@ export default View.extend({ const newModel = lodash.extend(model, { x: ui.position.left / this.width(), y: (ui.position.top - (this.height() / 2)) / this.width(), + containerWidth: this.width(), + containerHeight: this.height(), }); this.collection.add(newModel, { diff --git a/test/component-movable-test.js b/test/component-movable-test.js index 64c1533..2ea41c9 100644 --- a/test/component-movable-test.js +++ b/test/component-movable-test.js @@ -22,6 +22,16 @@ describe('Moveable component', () => { viewInstance.remove(); } + function mouseMoveTo(x, y) { + viewInstance.el.dispatchEvent(createMouseEvent({ type: 'mousedown' })); + document.dispatchEvent(createMouseEvent({ + type: 'mousemove', + clientX: x, + clientY: y, + })); + document.dispatchEvent(createMouseEvent({ type: 'mouseup' })); + } + before(cb => environment.then((w) => { document = w.document; cb(); @@ -44,13 +54,30 @@ describe('Moveable component', () => { moveableComponent({ view: viewInstance }); viewInstance.on('moveend', spy); equal(spy.called, false); - viewInstance.el.dispatchEvent(createMouseEvent({ type: 'mousedown' })); - document.dispatchEvent(createMouseEvent({ - type: 'mousemove', - clientX: 100, - clientY: 100, - })); - document.dispatchEvent(createMouseEvent({ type: 'mouseup' })); + mouseMoveTo(100, 100); equal(spy.calledWithExactly({ x: 150, y: 150 }), true); }); + + describe('API', () => { + it('Should return actual position of the element', () => { + const api = moveableComponent({ view: viewInstance }); + + equal(api.getPosition().x, 0); + equal(api.getPosition().y, 0); + mouseMoveTo(100, 100); + equal(api.getPosition().x, 150); + equal(api.getPosition().y, 150); + }); + + it('Should set position to given coordinates', () => { + const api = moveableComponent({ view: viewInstance }); + + equal(api.getPosition().x, 0); + equal(api.getPosition().y, 0); + api.moveTo({ x: 150, y: 150 }); + equal(api.getPosition().x, 150); + equal(api.getPosition().y, 150); + equal(viewInstance.el.style.transform, 'translate3d(150px, 150px, 0)'); + }); + }); });