anthonyshort / deku

Render interfaces using pure functions and virtual DOM
https://github.com/anthonyshort/deku/tree/master/docs
3.41k stars 131 forks source link

How to work with different DOM? #348

Open user471 opened 8 years ago

user471 commented 8 years ago

Deku has this Actions abstraction https://github.com/dekujs/deku/blob/master/src/diff/index.js#L11 Does it mean you can work with any dom and not just html? For example, I want to use deku with pixi.js library which has its own DOM. If it's possible, is there any example how can I do it?

anthonyshort commented 8 years ago

Yeah the diff functions just return an array of those actions. You can see how it's used in dom/patch.js. I think we'll need to expose the diff function if we're not already. Then you just need to make your own patch function.

I don't know much about pixi so I won't be that much help with that exactly.

user471 commented 8 years ago

Then you just need to make your own patch function

Why do I need my own patch? Can't I just refined only actions? Patch logic should be the same, go throw nodes and apply actions.

anthonyshort commented 8 years ago

Maybe I'm not following exactly what you meant. I thought you meant you wanted to create your own custom renderer. If you just want to apply it to some random piece of DOM, we could expose both the patch and diff functions and you can just use them on anything.

user471 commented 8 years ago

I am thinking about this ideal scenario

Pixi has its own object model, something like this

var pixiNode = new Container();
pixiNode.addChild(new Text("123"));
pixiNode.addChild(new Sprite("img.png"));

I can create virtual dom.

var vNode = element('container' [
  element('text', {text:"123"}),
  element('sprite', {img:"img.png"})
])
var vNodeNew = element('container' [
  element('text', {text:"456"}),
  element('sprite', {img:"img.png"})
])

then I would do something like this

var diffs = diff(vNode, vNodeNew);
patch(pixiNode, diffs); //it would change text "123" to "456" in pixiNode

Ideally, I would need to implement only two parts vNode to pixiNode converter

pixiNode = createElement(vNode);

and patch Actions, something like this

Actions = {
    setAttribute = function(pixiNode, name, value){
         ...
    }
    ....
}

All other logic should be the same as for html, so it could be reused.

Is deku support something like this?

anthonyshort commented 8 years ago

Yeah you should be able to do that. I'll reply with a bit more detail later today. You would still just need a custom patch function that takes the array of Actions and makes the appropriate changes to the pixiNode. That's essentially creating a Pixi renderer for Deku :)

andrewn commented 8 years ago

I also want to do this, either with Pixi.js or some other scenegraph-based library like Two.js or Fabric.js. So more detail on the approach would be great!

anthonyshort commented 8 years ago

This should be fairly straightforward, you'll just need to do something similar to the DOM renderer. You might have an API like this.

// This will function roughly the same as the DOM root element
let pixiNode = new Container()

// Create a function to render the lastest vnode
let render = createPixiRenderer(container)

You can follow the DOM renderer pretty closely, you'll need:

The update function would work something like this:

import {diff} from 'deku'
const {Actions, diffNode} = diff // This is the API we'd need to expose

export default function patch (pixiNode, action) {
    Actions.case({
      setAttribute: (name, value, previousValue) => {

      },
      removeAttribute: (name, previousValue) => {

      },
      insertBefore: (index) => {

      },
      sameNode: () => {

      },
      updateChildren: (changes) => {
        changes.forEach(change => {
          Actions.case({
            insertChild: (vnode, index, path) => {
              pixiNode.addChild(new Text(vnode.nodeValue))
            },
            removeChild: (index) => {

            },
            updateChild: (index, actions) => {

            }
          }, change)
        })
      },
      updateThunk: (prev, next, path) => {
        // If you want lazy-rendered vnodes
      },
      replaceNode: (prev, next, path) => {

      },
      removeNode: (prev) => {

      }
    }, action)
}

It shouldn't be too hard to put together. I'll make sure you can access the diff in the next release.

anthonyshort commented 8 years ago

Making a render for Two or Fabric would be similar. I might try getting something like Two working as it should be fairly simple.

user471 commented 8 years ago

Thanks.

I'll make sure you can access the diff in the next release.

When would it be? Also, according to http://vdom-benchmark.github.io/vdom-benchmark/ previous version of Deku was slower than other libraries. Were there any perfomance improvements in 2.0?