嵌套的 GSAP 可拖动对象不会在淘汰赛更新时更新
Nested GSAP draggables not updating on knockout update
所以我使用 GSAP TweenLite 和 Draggable,以及 KnockoutJS 来创建嵌套的可调整大小的可观察元素(一个 div
里面有一个 image
)。我的目标是通过数字输入以及拖动和调整大小来调整这些元素的大小和重新定位,就像 Java/Android 的布局生成器一样。我的代码目前允许我监视由拖动和调整大小操作引起的变化,但是它不会对 Knockout 绑定或可观察对象的更新做出反应。
Javascript:
function object(id) {
var self = this;
self.id = ko.observable(id);
self.parent = ko.observable();
self.childNodes = ko.observableArray([]);
self.x = ko.observable(100);
self.y = ko.observable(50);
self.w = ko.observable(100);
self.h = ko.observable(100);
}
function EditorViewModel() {
this.self = this;
self.Objects = ko.observableArray([new object("1")]);
self.Objects().push(new object("2"));
self.Objects()[0].childNodes.push(new object("1.1"));
self.Objects()[0].childNodes()[0].childNodes.push(new object("1.1.1"));
self.chosenObject = ko.observable();
ko.bindingHandlers.dragResize = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var val = ko.unwrap(valueAccessor());
var drag = element;
if (element.parentNode.parentNode == $(document.body)) {
parent = element.parentNode;
} else {
var parent = element.parentNode.parentNode;
}
var handle = $("<div class='resize-handle'></div>").appendTo(drag);
TweenLite.set(handle, {bottom: 0, right: 0});
Draggable.create(drag,
{
type: 'top left', bounds: parent,
onDragStart: function ()
{
if (typeof Draggable.get(parent) !== 'undefined') {
untilRange(element, false); //sets all parent Dragagbles to disabled to prevent quirks when dragging child
}
},
onDragEnd: function ()
{
if (typeof Draggable.get(parent) !== 'undefined') {
untilRange(element, true); //same as above, but re-enables them after drag
}
},
onDrag:function (e){
val.x(this.x);
val.y(this.y);
}
});
Draggable.create(handle, {
type: "top,left",
onPress: function (e) {
e.stopPropagation(); // cancel drag
},
onDrag: function (e) {
val.w(this.x);
val.h(this.y);
TweenLite.set(drag,{width:val.w(),height:val.h()});
},
});
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var val = ko.unwrap(valueAccessor());
var drag = Draggable.get(element);
element.x=val.x();
drag.update();
}
}
HTML:
<div id="range" data-bind="template:{name: 'elementTemplate',foreach:Objects()}">
</div>
<!--Templates-->
<script type="text/html" id="elementTemplate">
<!--<pre style="position:relative;width: 100px" data-bind="text: ko.toJSON($data, null, 2)"></pre>-->
<div class="draggable" data-bind="attr:{id:id}, dragResize:{x:x,y:y,h:h,w:w}">
<div data-bind="template:{name: 'elementTemplate',foreach:childNodes()}">
</div>
<img src="res/Download.jpg" class="mapClass" alt="If you see this we haven't done well"/>
</div>
</script>
如果有人对如何实现我的目标有不同的建议,请告诉我,我唯一想保留的部分是 KnockoutJS,因为我的老师推荐它。
你的代码看起来还不错。我无法 运行 它未经修改 - 例如我错过了 untilRange 函数。因此,我将其简化为最基本的要素,以测试如何让可观察对象更新位置,反之亦然。您将不得不重新申请您的原始代码。
您可能遇到的问题是您 运行 遇到过很多次的问题,当您尝试在 knockout 中进行新绑定时,会在两个方向上更新 observable:
- 你拖动你的对象来修改你的可观察对象
- 你修改了你的可观察对象,它修改了你的对象
有些情况下您需要注意,修改您的可观察对象的事件不会触发您的绑定更新。但这是推测,因为我无法 运行 未修改的代码。
仍然从您的代码中制作一个较小的示例,即使不考虑周期,双向更新似乎也能正常工作。我只发现在绑定的 update() 函数中我根本不会调用 Draggable.update() 。除此之外,双向绑定应该可以工作。
看到这个fiddle:
https://jsfiddle.net/domoran/1dk1hc4v/5/
Javascript:
function object(id) {
var self = this;
self.id = ko.observable(id);
self.x = ko.observable(0);
self.y = ko.observable(0);
}
ko.bindingHandlers.dragResize = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var val = ko.unwrap(valueAccessor());
var lastSelected = val.last;
Draggable.create(element, {
type: "x,y",
onDrag: function(e) {
lastSelected(ko.dataFor(element));
val.x(this.x);
val.y(this.y);
}
});
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var updateCoords = function() {
// since this is invoked inside a computed, it will be invoked,
// whenever the observables x and y change
TweenLite.set(element,{x:val.x(),y:val.y()});
};
var val = ko.unwrap(valueAccessor());
// make the updateCoors function call whenever one of its observables
// changes
updateCoords();
}
};
function EditorViewModel() {
var self = this;
self.Objects = ko.observableArray([new object("1"), new object("2")]);
self.lastSelected = ko.observable();
}
ko.applyBindings(new EditorViewModel());
HTML:
<body>
<div id="range" data-bind="template:{name: 'elementTemplate',foreach:Objects }">
</div>
<!--Templates-->
<script type="text/html" id="elementTemplate">
<div data-bind="attr:{id:id}, dragResize:{x:x,y:y,last:$root.lastSelected}">
<img width=80 src="https://upload.wikimedia.org/wikipedia/commons/4/4c/World_borders_lamb_azi.png" class="mapClass" alt="If you see this we haven't done well" /><span data-bind="text: x() + '/' + y()"></span>
</div>
</script>
<div data-bind="with:lastSelected">
<label>x</label><input type="text" data-bind="value: x" />
<label>y</label><input type="text" data-bind="value: y" />
</div>
</body>
所以我使用 GSAP TweenLite 和 Draggable,以及 KnockoutJS 来创建嵌套的可调整大小的可观察元素(一个 div
里面有一个 image
)。我的目标是通过数字输入以及拖动和调整大小来调整这些元素的大小和重新定位,就像 Java/Android 的布局生成器一样。我的代码目前允许我监视由拖动和调整大小操作引起的变化,但是它不会对 Knockout 绑定或可观察对象的更新做出反应。
Javascript:
function object(id) {
var self = this;
self.id = ko.observable(id);
self.parent = ko.observable();
self.childNodes = ko.observableArray([]);
self.x = ko.observable(100);
self.y = ko.observable(50);
self.w = ko.observable(100);
self.h = ko.observable(100);
}
function EditorViewModel() {
this.self = this;
self.Objects = ko.observableArray([new object("1")]);
self.Objects().push(new object("2"));
self.Objects()[0].childNodes.push(new object("1.1"));
self.Objects()[0].childNodes()[0].childNodes.push(new object("1.1.1"));
self.chosenObject = ko.observable();
ko.bindingHandlers.dragResize = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var val = ko.unwrap(valueAccessor());
var drag = element;
if (element.parentNode.parentNode == $(document.body)) {
parent = element.parentNode;
} else {
var parent = element.parentNode.parentNode;
}
var handle = $("<div class='resize-handle'></div>").appendTo(drag);
TweenLite.set(handle, {bottom: 0, right: 0});
Draggable.create(drag,
{
type: 'top left', bounds: parent,
onDragStart: function ()
{
if (typeof Draggable.get(parent) !== 'undefined') {
untilRange(element, false); //sets all parent Dragagbles to disabled to prevent quirks when dragging child
}
},
onDragEnd: function ()
{
if (typeof Draggable.get(parent) !== 'undefined') {
untilRange(element, true); //same as above, but re-enables them after drag
}
},
onDrag:function (e){
val.x(this.x);
val.y(this.y);
}
});
Draggable.create(handle, {
type: "top,left",
onPress: function (e) {
e.stopPropagation(); // cancel drag
},
onDrag: function (e) {
val.w(this.x);
val.h(this.y);
TweenLite.set(drag,{width:val.w(),height:val.h()});
},
});
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var val = ko.unwrap(valueAccessor());
var drag = Draggable.get(element);
element.x=val.x();
drag.update();
}
}
HTML:
<div id="range" data-bind="template:{name: 'elementTemplate',foreach:Objects()}">
</div>
<!--Templates-->
<script type="text/html" id="elementTemplate">
<!--<pre style="position:relative;width: 100px" data-bind="text: ko.toJSON($data, null, 2)"></pre>-->
<div class="draggable" data-bind="attr:{id:id}, dragResize:{x:x,y:y,h:h,w:w}">
<div data-bind="template:{name: 'elementTemplate',foreach:childNodes()}">
</div>
<img src="res/Download.jpg" class="mapClass" alt="If you see this we haven't done well"/>
</div>
</script>
如果有人对如何实现我的目标有不同的建议,请告诉我,我唯一想保留的部分是 KnockoutJS,因为我的老师推荐它。
你的代码看起来还不错。我无法 运行 它未经修改 - 例如我错过了 untilRange 函数。因此,我将其简化为最基本的要素,以测试如何让可观察对象更新位置,反之亦然。您将不得不重新申请您的原始代码。
您可能遇到的问题是您 运行 遇到过很多次的问题,当您尝试在 knockout 中进行新绑定时,会在两个方向上更新 observable: - 你拖动你的对象来修改你的可观察对象 - 你修改了你的可观察对象,它修改了你的对象
有些情况下您需要注意,修改您的可观察对象的事件不会触发您的绑定更新。但这是推测,因为我无法 运行 未修改的代码。
仍然从您的代码中制作一个较小的示例,即使不考虑周期,双向更新似乎也能正常工作。我只发现在绑定的 update() 函数中我根本不会调用 Draggable.update() 。除此之外,双向绑定应该可以工作。
看到这个fiddle: https://jsfiddle.net/domoran/1dk1hc4v/5/
Javascript:
function object(id) {
var self = this;
self.id = ko.observable(id);
self.x = ko.observable(0);
self.y = ko.observable(0);
}
ko.bindingHandlers.dragResize = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var val = ko.unwrap(valueAccessor());
var lastSelected = val.last;
Draggable.create(element, {
type: "x,y",
onDrag: function(e) {
lastSelected(ko.dataFor(element));
val.x(this.x);
val.y(this.y);
}
});
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
var updateCoords = function() {
// since this is invoked inside a computed, it will be invoked,
// whenever the observables x and y change
TweenLite.set(element,{x:val.x(),y:val.y()});
};
var val = ko.unwrap(valueAccessor());
// make the updateCoors function call whenever one of its observables
// changes
updateCoords();
}
};
function EditorViewModel() {
var self = this;
self.Objects = ko.observableArray([new object("1"), new object("2")]);
self.lastSelected = ko.observable();
}
ko.applyBindings(new EditorViewModel());
HTML:
<body>
<div id="range" data-bind="template:{name: 'elementTemplate',foreach:Objects }">
</div>
<!--Templates-->
<script type="text/html" id="elementTemplate">
<div data-bind="attr:{id:id}, dragResize:{x:x,y:y,last:$root.lastSelected}">
<img width=80 src="https://upload.wikimedia.org/wikipedia/commons/4/4c/World_borders_lamb_azi.png" class="mapClass" alt="If you see this we haven't done well" /><span data-bind="text: x() + '/' + y()"></span>
</div>
</script>
<div data-bind="with:lastSelected">
<label>x</label><input type="text" data-bind="value: x" />
<label>y</label><input type="text" data-bind="value: y" />
</div>
</body>