[SOLVED] Unload Hype animation ( vue, react, nuxt, next )

Hello All !

I’m playing around with hype and vuejs and it works really well !
I have a little issue though, when leaving the page with the animation, the animation dom is destroyed but the different event listeners are not removed causing a quantity of logs to appear in the console.
I already manage to access the animation to play and pause it but I tried to look in the documentation and in the forum and i could not find anything about unloading a Hype Document.

Would anyone know about a way of doing this in javascript outside of the hype animation ?

thanks in advance

This is a known issue… search the forum on VUE or GARBAGE COLLECTION.

Regards

Thanks for the quick reply !

Although the issue seems to be related to the fact that none of the event listeners are removed and that there are absolutely no function in the api that allows the removal or unload of the animation.

regards

1 Like

For now we are stuck with that situation. Meaning you can do a manual cleanup as far as possible and rely on the browser todo the rest … and hope for not to many refresh from your shadow DOM as that drains on the performance. Did that while embedding Hype into the Twine Engine (was neglectable in that case). Other solution is to embed a VUE app into Hype (other way around) avoiding reloads but triggering things from VUE in direction towards Hype (living outside of the VUE managed scope). Not ideal at this point but a possibility …

Hopefully we get some sort of clean exit/unload etc. one day.

Allright,

to give you more context i’m currently working with nuxt (an ssr wrapper for vue). I’m inserting the Hype animation within the page using the div in the generated html document and inserting the script whenever the div is rendered and available and adding an event listener to play the animation whenever all the elements are loaded.

I’m going to try and embedding the Html file within an iframe hoping that the event listeners will be bound to it and that destroying it will be working. (I’ll keep you informed of the progress)

Would you happend to know for what reason the unload methods do not exist ? (Is it more of a technical limitation due to the way the js animation is generated or is it not yet implemented ?)
[ any insights on the subject @jonathan @Daniel ? ]

flattering words :slight_smile:

1 Like

Highly technical term… :man_student: :nerd_face:

(Let's stay on topic.)

In the needs of Vue/React, a Hype "unload" feature would most likely have the meaning of keeping all the state, but performing an entire document pause. The Hype runtime would still be in memory, but inactive. When loaded back, it would rebuild the DOM based on the saved state and resume the animations. This would handle a case of wanting to go back exactly where you were in the Hype document.

Primarily this isn't implemented because Hype was built in a time before React existed and didn't have to deal with the DOM changing behind its back. Hype always assumes its DOM is there :slight_smile:.

I wouldn't give any promises to a release date or commitment on the feature, but I'd definitely like to play nice with DOM/React.

The other method you describe should work fine -- just use an iframe. This won't let you save/restore state, but you can do this yourself if need be.

So Hype can handle its animations in React, but not the more useful case of returning where one left off.

Hi again !

Thanks for the thorough answer @jonathan, really appreciate!

So i’ve embed the exported animation inside an iFrame like recommended and it works like a charm ( doesn’t feel really clean though hahah ).
As the animation state doesn’t matter to me it was the perfect solution ( and i can still access the hype context within the iframe using iframe.contentWindow to go to a specific moment of the animation :ok_hand:).

Thanks for all,
Keep up with the good work

Lafeuille

2 Likes

Great, glad that was a solution for you!

1 Like

Using an Iframe is not a viable solution for us as we were triggering animations on scroll.
Today, using a Hype export in React remains either a fragile or complex task when needing to "support" a dozen files to be embedded in different environments.
We started using Hype extensively to empower our design team to produce animated data spots, so we really hope to see an update soon. Or anyway, before having to switch to a different solution. That'd be a shame.

while using iframes: postMessage as communication-channel to trigger animations is no option?

It something we want to explore. Moving the scroll logic to the React side it is certainly doable but it will also require editing/setting up the Hype documents to support this specific use case which is what I fear will be trickier to manage for our internal design team. .

We adopted Hype to empower people with no coding background in creating interactive artefacts easily ... when complexity grows to the point of requiring a significant amount of coding, it starts becoming easier to think about adopting a completely different workflow. At least when it comes to data visualisation, which has been our main use case to date.

if the coding part regarding the scroll/intersection is outside of the hype-part it'll be as easy (even easier) for the non-coding-designers :slight_smile:

@h_classen if I ask "who likes iFrames?", will you remind me of Youtube? :smiley_cat:
It would be nice to see a native unload function :crossed_fingers:

1 Like

UPDATE for anyone facing similar challenges:

We are trying to integrate Hype within a Gatsby website with content stored in MDX files.
Our Hype animation embeds need to be fully responsive and play when scrolling in view.

We ended up using React Interaction Observer to start the timeline when the Hype iFrame enters the viewport. https://github.com/thebuilder/react-intersection-observer
We use React iFrame Resizer to handle the responsiveness of the iframe as well as sending messages from/to the iframe https://github.com/davidjbradshaw/iframe-resizer-react
Our Hype component looks something like this:

import React, { useRef }  from "react"
import IframeResizer from 'iframe-resizer-react'
import { InView } from 'react-intersection-observer'

const HypeIframe = ({ animationName }) => {

  const ref = useRef(null);

  const trigPlay = (data) => {
    ref.current.sendMessage(data)
  };

  return (
  
  <InView as="div" onChange={(inView, entry) => trigPlay('Play Hype')}>
    <IframeResizer
    frameBorder='0'
    forwardRef={ref}
    onMessage={trigPlay}
    className='mt-3 mb-3'
    src={`/hype/${animationName}/index.html`}
    style={{ width: '1px', minWidth: '100%'}}
    />
  </InView>
)};

export default HypeIframe

Then in our Hype document head, we have:

window.iFrameResizer = {
	  onMessage: function(message){ 
	    if (message === "Play Hype") {
	    	hypeDocument.continueTimelineNamed('Main Timeline', hypeDocument.kDirectionForward, true);
	  	}
	  }
	}

function removeStyle() {
  const docTitleNode = document.getElementsByTagName("title")[0];
  const heightStyleTag = docTitleNode.nextElementSibling;
  heightStyleTag.remove();
}

if (window.addEventListener) {
    window.addEventListener('load', removeStyle, false); 
} else if (window.attachEvent) {
    window.attachEvent('onload', removeStyle);
}

As mentioned earlier, we need to support the full responsiveness of Hype documents with multiple layouts and variable dimensions. For this reason, our complete function integrates @h_classen
Creating a Flexible Tumult Hype Document within a DIV with no set ‘height’

    var _layouts = hypeDocument.layoutsForSceneNamed(hypeDocument.currentSceneName());
    var layoutName = hypeDocument.currentLayoutName();

    var res = null;

    for (var i = 0; i < _layouts.length; i++) {
        var Obj = _layouts[i];
            if (Obj.name === layoutName) {
                res = Obj;
            break;
        }
    }

    if (res) {
    resizeHype();
    }
    
    window.iFrameResizer = {
	  onMessage: function(message){ 
	    if (message === "Play Hype") {
	    	hypeDocument.continueTimelineNamed('Main Timeline', hypeDocument.kDirectionForward, true);
	  	}
	  }
	}

    function removeStyle() {
      const docTitleNode = document.getElementsByTagName("title")[0];
      const heightStyleTag = docTitleNode.nextElementSibling;
      heightStyleTag.remove();
    }

    function resizeHype() {
        var ratioScale = res['width'] / res['height'];
        var hypeEl = document.getElementById(hypeDocument.documentId());
        var currentWidth = hypeEl.offsetWidth;
        var newHeight = currentWidth / ratioScale;
        hypeEl.style.height = newHeight + 'px';
        hypeDocument.relayoutIfNecessary();
    }

    if (window.addEventListener) {
        window.addEventListener('load', resizeHype, false); 
        window.addEventListener('load', removeStyle, false); 
        window.addEventListener('resize', resizeHype, false); 
    } else if (window.attachEvent) {
        window.attachEvent('onload', removeStyle);
        window.attachEvent('onload', resizeHype);
        window.attachEvent('onresize', resizeHype);
    }

    resizeHype();

    return false
5 Likes

thx for sharing :slight_smile: :+1:

2 Likes