Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

renderToNode (or renderToElement) #15

Open
1 of 3 tasks
apla opened this issue Sep 8, 2019 · 5 comments
Open
1 of 3 tasks

renderToNode (or renderToElement) #15

apla opened this issue Sep 8, 2019 · 5 comments

Comments

@apla
Copy link

apla commented Sep 8, 2019

Hi,

I'm submitting a ...

  • bug report
  • feature request
  • other (Please do not submit support requests here (below))

Within hyperapp sometimes I need to make some preparations, to ensure everything is going fine with DOM/events/data. For example:

  1. generate <video> element, append some sources, and check which one browser wants to load;
  2. create <img> element and check if image is accessible and what size they have;
  3. insert svg fragment into already loaded though <object> tag svg dom.

It is really useful to have function, which renders h('video', ...) into new Element, which can be inserted into existing DOM or just loaded (like images or videos), because as of now my preparations looks like

const el = document.createElement('video');
el.addEventListener('loadeddata', this.onVideoLoad.bind(this), false);
el.addEventListener('abort', this.onVideoAbort.bind(this), false);
el.addEventListener('waiting', this.onVideoWaiting.bind(this), false);
el.addEventListener('loadedmetadata', this.onVideoMeta.bind (this), false);
el.setAttribute('preload', 'metadata');
el.setAttribute('muted', 'true');
el.setAttribute('playsinline', 'true');
this.config.sources.forEach(source => {
	var srcEl = document.createElement('source');
	srcEl.src = patchLocalVideo(source.url);
	srcEl.type = source.type;
	el.appendChild(srcEl);
});

I can implement it myself and make a pull request if you're interested in this.

@frenzzy
Copy link
Member

frenzzy commented Sep 9, 2019

Sorry, but I do not understand how this is related to hyperapp-render package? It just renders your VDOM to string and that's it, for adding event listeners, changing nodes and attributes you should use hyperapp package. Also it is not clear why do you want to patch some urls in runtime, why can't you provide correct urls from the beginning.

How I see your code should look like:

// app.js
import { h } from 'hyperapp'

export const state = {
  sources: [/*...*/]
}

export const actions = {
  onVideoLoad() {/*...*/},
  onVideoAbort() {/*...*/},
  onVideoWaiting() {/*...*/},
  onVideoMeta() {/*...*/},
}

export const view = (state, actions) => (
  <video
    onloadeddata={actions.onVideoLoad}
    onabort={actions.onVideoAbort}
    onwaiting={actions.onVideoWaiting}
    onloadedmetadata={actions.onVideoMeta}
    preload="metadata"
    muted="true"
    playsinline="true"
  >
    {state.sources.map((source) => (
      <source key={source.url} src={source.url} type={source.type} />
    ))}
  </video>
)
// client.js
import { app } from 'hyperapp'
import { state, actions, view } from './app'
app(state, actions, view, document.getElementById('app'))
// server.js
import http from 'http'
import { renderToString } from 'hyperapp-render'
import { state, actions, view } from './app'
http.createServer((req, res) => {
  res.setHeader('Content-Type', 'text/html')
  res.end(renderToString(
    <html>
      <head><title>Video</title></head>
      <body>
        <div id="app">{view(state, actions)}</div>
      </body>
    </html>
  ))
}).listen(8080)

P.S.: You can use hyperapp-starter to test it.

@apla
Copy link
Author

apla commented Sep 9, 2019

hyperapp will render those elements visible. I need to preload elements outside of document to ensure incoming data is right (for example, video have exact resolution of 5520x1320 pixels) or to cache it (for example, set image src and wait it loaded) and only after this step push data into hyperapp view.

I can hide those elements or put outside browser view box, but it is huge slowdown, ui freeze and sometimes browser crash when you have many high resolution images and videos on page. When browser already loaded data from videos and images, everything goes smooth.

Why hyperapp-render? This package prepares HTML/SVG text to be hydrated before app renders view, and renderElement will prepare DOM node before app renders view with the same jsx/h syntax.

So, my example will look like this:

// client.js
import { app } from 'hyperapp'
import { state, actions, view, preloadVideo } from './app'
app(state, actions, view, document.getElementById('app'))

const appData = {
  videos: [{
    sources: [{
      url: "1.mp4",
      type: "video/mp4"
    }, {
      url: "1.webm",
      type: "video/webm"
    }]
  }]
};

// This should work as part of subscription, but I still learning how to do that
Promise.all (appData.videos.map (v => preloadVideo(v))).then (videoEvents => {
  // browser will load one of videos by type:
  // safari for mac and ios + ie will load h.264
  // chrome + ff will load webm
  // but currentSrc will definitely contain playable video
  changeState (state => (videos: videoEvents.map (event => event.target.currentSrc)))
})


// app.js
import { h } from 'hyperapp'

export const state = {
  videos: []
}

export const actions = {
  onVideoLoad() {/*...*/},
  onVideoAbort() {/*...*/},
  onVideoWaiting() {/*...*/},
  onVideoMeta() {/*...*/},
}

export const view = (state, actions) => (
  <video
    onloadeddata={actions.onVideoLoad}
    onabort={actions.onVideoAbort}
    onwaiting={actions.onVideoWaiting}
    onloadedmetadata={actions.onVideoMeta}
    preload="metadata"
    muted="true"
    playsinline="true"
  >
    {state.sources.map((source) => (
      <source key={source.url} src={source.url} type={source.type} />
    ))}
  </video>
)

export function preloadVideo (videoParams) {
return new Promise ((resolve, reject) => {
  const videoEl = renderElement(<video
    onloadeddata={resolve}
    preload="metadata"
    muted="true"
    playsinline="true"
  >
    {state.sources.map((source) => (
      <source key={source.url} src={source.url} type={source.type} />
    ))}
  </video>);
  videoEl.load()
});
}

@frenzzy
Copy link
Member

frenzzy commented Sep 9, 2019

I still do not see why/how you use hyperapp-render. If you want to display some view after videos or images are loaded, you don't need hyperapp-render, just use hyperapp:

import { app, h } from 'hyperapp'

const state = {
  videoIsReadyToRender: false
}

const actions = {
  load() {
    // (pre)load video here
    return { videoIsReadyToRender: true }
  }
}

const view = (state, actions) => (
  <div onload={actions.load}>
    {state.videoIsReadyToRender ? <YourVideo /> : <b>Loading video...</b>}
  </div>
)

app(state, actions, view, document.body)

@apla
Copy link
Author

apla commented Sep 9, 2019

DINAMO-loading

@frenzzy
Copy link
Member

frenzzy commented Sep 9, 2019

😁Let's go to Slack and discuss in chat mode quickly (my nickname is @frenzzy)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants