Skip to content

Commit ac163df

Browse files
committed
added velocity animations, before removing options for performance testing
1 parent 15d35b5 commit ac163df

File tree

6 files changed

+195
-72
lines changed

6 files changed

+195
-72
lines changed

API.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
API
2+
=========
3+
4+
Domsetter
5+
---------
6+
The domsetter object is the main object. It is either bound to window.domsetter or it can be obtained using requirejs.
7+
8+
Domsetter.h
9+
-----------
10+
function h(
11+
string selector,
12+
[object properties],
13+
[array children]
14+
)
15+
16+
The h method is used to create a virtual DOM node. This method is largely inspired by the mercuryjs and mithril frameworks.
17+
The `selector` parameter contains the tagName, id and fixed css classnames in CSS selector format.
18+
It is formatted as follows: `tagname.cssclass1.cssclass2#id`.
19+
The properties parameter is an object literal that contains the properties to set on the DOM nodes.
20+
Additional conditional css classes can be provided using a `properties.classes` object with className properties with
21+
boolean values. For example: `h('div', {classes: {class1: true, class2: false} }, [])`
22+
The `children` parameter is an array of virtual DOM nodes to add as children. This array may contain nested arrays and null or undefined values.
23+
Nested arrays are flattened and null and undefined values will be skipped.
24+
25+
In order for domsetter to be able to apply transitions and achieve high performance there are additional
26+
requirements to meet:
27+
28+
* All children should either have a unique selector, or have a unique `key` property in their properties object.
29+
* The `properties` object must always have the same keys in subsequent renderings.
30+
When properties are to be cleared, they must be set to null or undefined.
31+
* The `properties.classes` object must always have the same keys in subsequent renderings.
32+
33+
Domsetter.createDom
34+
-------------------
35+
function createDom(
36+
VNode vnode,
37+
[object options]
38+
) : Mount
39+
40+
The createDom method creates a real DOM given a VNode. It returns a Mount object. VNodes may only be mounted once.
41+
this method returns a Mount object. The resulting DOM node will be present under mount.domNode.
42+
This is a low-level method. Users wil typically use Domsetter.renderLoop instead.
43+
44+
Domsetter.mergeDom
45+
------------------
46+
function mergeDom(
47+
Element element,
48+
VNode vnode,
49+
[object options]
50+
)
51+
52+
The mergeDom method created the real DOM from the VNode. The resulting
53+
54+
Mount
55+
-----
56+
57+
The mount object represents a VNode tree that has been converted to a real DOM tree. It provides the following methods:
58+
59+
Mount.update
60+
------------
61+
62+
function update(
63+
VNode updatedVNode
64+
)
65+
66+
This function updates the mounted VNode to another VNode from a subsequent rendering.
67+
68+
Mount.domNode
69+
-------------
70+
Node domNode
71+
72+
The Mount.domNode provides access to the DOM Node that has been rendered.
73+

bower.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,8 @@
2424
"test",
2525
"tests",
2626
"examples"
27-
]
27+
],
28+
"dependencies": {
29+
"velocity": "~1.1.0"
30+
}
2831
}

index.html renamed to demo.html

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,52 +19,34 @@
1919
}
2020
</style>
2121
<script src="domsetter.js"></script>
22+
<script src="bower_components/velocity/velocity.js"></script>
23+
<script src="bower_components/velocity/velocity.ui.js"></script>
24+
<script src="velocity-transitions.js"></script>
2225
<script src="showStats.js"></script>
2326
<script>
2427
var h = window.domsetter.h;
2528

26-
var simplisticDiff = {
27-
nodeToRemove: function (node) {
28-
node.classList.add("removed");
29-
setTimeout(function () {
30-
node.parentNode.removeChild(node);
31-
}, 500);
32-
},
33-
nodeAdded: function (node) {
34-
node.classList.add("added");
35-
setTimeout(function () {
36-
node.classList.remove("added");
37-
}, 500);
38-
},
39-
nodeUpdated: function (node) {
40-
node.classList.add("updated");
41-
setTimeout(function () {
42-
node.classList.remove("updated");
43-
}, 500);
44-
}
45-
};
46-
4729
document.addEventListener('DOMContentLoaded', function () {
4830
var items = [];
4931
for (var i = 0; i < 20; i++) {
5032
items.push({ title: "Item " + i, visible: i % 2 === 0, id: i });
5133
}
5234
var render = function () {
5335
return h("div", {}, items.filter(function (item) { return item.visible; }).map(function (item) {
54-
return h("div.item", { key: "item-" + item.id }, [item.title]);
36+
return h("div.item", { key: "item-" + item.id, enterAnimation: "slideDown", exitAnimation: "slideUp" }, [item.title]);
5537
}));
5638
};
5739

58-
var mount = window.domsetter.createDom(render());
40+
var mount = window.domsetter.createDom(render(), { transitions: window.velocityTransitions });
5941
document.body.appendChild(mount.domNode);
6042

6143
setTimeout(function () {
6244
items[16].visible = false;
63-
mount.update(render(), simplisticDiff);
45+
mount.update(render());
6446
setTimeout(function () {
6547
items[1].visible = true;
6648
items[16].visible = true;
67-
mount.update(render(), simplisticDiff);
49+
mount.update(render());
6850
}, 700);
6951
}, 300);
7052
});

domsetter.js

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"use strict";
44

55
// constant flags
6-
var delayInsertDom = false; // To be verified which is fastest
6+
var delayInsertDom = false; // don't care
77
var skipUniqueSelectorCheck = false;
88
var clearOldProperties = false; // only for nodes without a key
99
var checkEqualsBeforeAssigningProperty = true; // true is fastest!
@@ -28,36 +28,37 @@
2828

2929
// Hyperscript helper functions
3030

31-
var flattenInto = function (parentSelector, insertions, main, mainIndex) {
31+
var flattenInto = function (parentSelector, insertions, main, mainIndex, selectorsThusFar) {
3232
for (var i = 0; i < insertions.length; i++) {
3333
var item = insertions[i];
3434
if (Array.isArray(item)) {
3535
mainIndex = flattenInto(item, main, mainIndex);
3636
} else {
37-
checkForDuplicates(parentSelector, main, item, mainIndex);
37+
checkForDuplicateSelectors(parentSelector, item, selectorsThusFar);
3838
main.splice(mainIndex, 0, item);
3939
mainIndex++;
4040
}
4141
}
4242
return mainIndex;
4343
};
4444

45-
var checkForDuplicates = function (parentSelector, children, sameAs, endAt) {
45+
var checkForDuplicateSelectors = function (parentSelector, sameAs, selectorsThusFar) {
4646
if (skipUniqueSelectorCheck) {
4747
return;
4848
}
49-
if (sameAs.hasOwnProperty("text")) {
49+
var selector = sameAs.vnodeSelector;
50+
if (selector === "") {
5051
return; // textnodes can be safely ignored.
5152
}
52-
for (var i = 0; i < endAt; i++) {
53-
if (same(children[0], sameAs)) {
54-
if (!sameAs.properties || !sameAs.properties.key) {
55-
throw new Error("[" + parentSelector + "] contains undistinguishable child nodes [" + sameAs.vnodeSelector + "], please add a key property.");
56-
} else {
57-
throw new Error("[" + parentSelector + "] contains two children with the same key:" + sameAs.properties.key);
58-
}
53+
if(sameAs.properties && sameAs.properties.key) {
54+
return; // uniqueness of keys is not checked for performance sake
55+
}
56+
for (var i = 0; i < selectorsThusFar.length; i++) {
57+
if (selector === selectorsThusFar[i]) {
58+
throw new Error("[" + parentSelector + "] contains undistinguishable child nodes [" + selector + "], please add unique key properties.");
5959
}
6060
}
61+
selectorsThusFar.push(selector);
6162
};
6263

6364
var toTextVNode = function (data) {
@@ -81,15 +82,16 @@
8182
}
8283
}
8384
var index = 0;
85+
var selectorsThusFar = [];
8486
while (index < children.length) {
8587
var child = children[index];
8688
if (!child) {
8789
children.splice(index, 1);
8890
} else if (Array.isArray(child)) {
8991
children.splice(index, 1);
90-
index = flattenInto(parentSelector, child, children, index);
92+
index = flattenInto(parentSelector, child, children, index, selectorsThusFar);
9193
} else if (child.hasOwnProperty("vnodeSelector")) {
92-
checkForDuplicates(parentSelector, children, child, index);
94+
checkForDuplicateSelectors(parentSelector, child, selectorsThusFar);
9395
index++;
9496
} else {
9597
children[index] = toTextVNode(child);
@@ -103,7 +105,7 @@
103105

104106
var classIdSplit = /([\.#]?[a-zA-Z0-9_:-]+)/;
105107

106-
var immediateDiff = {
108+
var immediateTransitions = {
107109
nodeToRemove: function (node) {
108110
node.parentNode.removeChild(node);
109111
},
@@ -113,7 +115,7 @@
113115

114116
var defaultOptions = {
115117
namespace: null,
116-
diff: immediateDiff
118+
transitions: immediateTransitions
117119
};
118120

119121
var applyDefaultOptions = function (options) {
@@ -259,7 +261,7 @@
259261
}
260262
oldChildren = oldChildren || emptyArray;
261263
newChildren = newChildren || emptyArray;
262-
var diff = options.diff;
264+
var transitions = options.transitions;
263265

264266
var oldIndex = 0;
265267
var newIndex = 0;
@@ -275,7 +277,7 @@
275277
if (findOldIndex >= 0) {
276278
// Remove preceding missing children
277279
for (i = oldIndex; i < findOldIndex; i++) {
278-
diff.nodeToRemove(oldChildren[i].domNode);
280+
transitions.nodeToRemove(oldChildren[i].domNode);
279281
}
280282
updateDom(oldChildren[findOldIndex], newChild, options);
281283
oldIndex = findOldIndex + 1;
@@ -289,23 +291,23 @@
289291
domNode.appendChild(childDomNode);
290292
}
291293
}, options);
292-
diff.nodeAdded(newChild.domNode);
294+
transitions.nodeAdded(newChild.domNode);
293295
}
294296
}
295297
newIndex++;
296298
}
297299
if (oldChildren.length > oldIndex) {
298300
// Remove child fragments
299301
for (i = oldIndex; i < oldChildren.length; i++) {
300-
diff.nodeToRemove(oldChildren[i].domNode);
302+
transitions.nodeToRemove(oldChildren[i].domNode);
301303
}
302304
}
303305
};
304306

305307
var createDom = function (vnode, afterCreate, options) {
306308
if (vnode.vnodeSelector === "") {
307309
vnode.domNode = document.createTextNode(vnode.text);
308-
!delayInsertDom && afterCreate(vnode.domNode);
310+
afterCreate(vnode.domNode);
309311
} else {
310312
var domNode, part, i, type;
311313
var tagParts = vnode.vnodeSelector.split(classIdSplit);

examples/todomvc/benchmark.html

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -37,39 +37,54 @@
3737
return window.renderTodosView(viewModel);
3838
}, { /* No render options */ });
3939

40-
var i;
4140
var newTodo = document.querySelector("#new-todo");
4241
newTodo.focus();
4342

44-
var start = window.performance.now();
45-
console.log("Adding todos");
46-
for (i = 0; i < 50; i++) {
47-
var inputEvent = document.createEvent('Event');
48-
inputEvent.initEvent('input', true, true);
49-
newTodo.value = 'Domsetter ------- Something to do ' + i;
50-
newTodo.dispatchEvent(inputEvent);
43+
var run = function () {
44+
var i;
45+
var start = window.performance.now();
46+
console.log("Adding todos");
47+
for(i = 0; i < 50; i++) {
48+
var inputEvent = document.createEvent('Event');
49+
inputEvent.initEvent('input', true, true);
50+
newTodo.value = 'Domsetter ------- Something to do ' + i;
51+
newTodo.dispatchEvent(inputEvent);
5152

52-
var keydownEvent = document.createEvent('Event');
53-
keydownEvent.initEvent('keypress', true, true);
54-
keydownEvent.keyCode = 13; // VK_ENTER
55-
newTodo.dispatchEvent(keydownEvent);
56-
}
53+
var keydownEvent = document.createEvent('Event');
54+
keydownEvent.initEvent('keypress', true, true);
55+
keydownEvent.keyCode = 13; // VK_ENTER
56+
newTodo.dispatchEvent(keydownEvent);
57+
}
5758

58-
console.log("Clicking checkboxes");
59-
var checkboxes = document.querySelectorAll('.toggle');
60-
for(i = 0; i < checkboxes.length; i++) {
61-
checkboxes[i].click();
62-
}
59+
console.log("Clicking checkboxes");
60+
var checkboxes = document.querySelectorAll('.toggle');
61+
for(i = 0; i < checkboxes.length; i++) {
62+
checkboxes[i].click();
63+
}
6364

64-
console.log("Deleting items");
65-
var deleteButtons = document.querySelectorAll('.destroy');
66-
for(i = deleteButtons.length - 1; i > -1; i--) {
67-
deleteButtons[i].click();
68-
}
69-
console.log("Deleting done");
70-
console.log("Running time: "+(window.performance.now() - start));
65+
console.log("Deleting items");
66+
var deleteButtons = document.querySelectorAll('.destroy');
67+
for(i = deleteButtons.length - 1; i > -1; i--) {
68+
deleteButtons[i].click();
69+
}
70+
console.log("Deleting done");
71+
var result = (window.performance.now() - start);
72+
console.log("Running time: " + result);
73+
return result;
74+
};
75+
76+
run();
77+
run();
78+
run();
79+
var result = 0;
80+
for (var i = 0; i < 10; i++) {
81+
result = result * i + run();
82+
result = result / (i + 1);
83+
};
84+
console.log("FINAL RESULT: " + result);
7185
});
7286

87+
7388
</script>
7489

7590
</body>

0 commit comments

Comments
 (0)