Using Adobe Edge Animate with AngularJS and AngularUI Router
Asked Answered
H

1

26

I'm working on a project at the moment which will incorporate multiple animations created with Adobe Edge Animate into an single AngularJS application. The idea is that these animations will act as the visuals for a game and I will control the composition based on input from the player. It's taken some experimenting to get to this stage but all of that is working perfectly.

My problems start whenever the player chooses to exit the current view and access it for a second time. For some reason this causes an issue with the Adobe Edge Animate Javascript API and the composition fails to load.

Essentially, I can load a composition once but no more. Whenever I attempt to load the composition for a second time I get the following Javascript errors...

Uncaught TypeError: Cannot read property 'stage' of undefined    edge.5.0.1.js:4872
Uncaught TypeError: Cannot read property 'stage' of undefined    edge.5.0.1.js:4519

I'm currently loading the composition directly from within the controller as follows...

.controller('GameTest', function($scope, $state) {
    AdobeEdge.loadComposition('edge-animate/GameTest', 'GameTest', {
      scaleToFit: "width",
      centerStage: "none",
      minW: "0",
      maxW: "undefined",
      width: "2048px",
      height: "1134px"
    }, {dom: [ ]}, {dom: [ ]});
})

I've also disabled caching for this state...

.state('game-test', {
  cache: false,
  url: "/games/test",
  controller: 'GameTest',
  templateUrl: 'templates/games/test.html'
})

All suggestions welcome and appreciate any help!

Thanks

Update: I've worked out a solution! Hopefully...

The issue seems to be resolved if the browser handles the contents of the relevant *_edge.js again. Since these are injected into the <head> whenever AdobeEdge.loadComposition(...) is called (via yepnope), and there doesn't appear to be any method of asking yepnope to reload injected Javascript, I have written a small service for Angular that I can use to handle this in place of the standard AdobeEdge.loadComposition(...) function. It's essentially a wrapper that will do the relevant checks beforehand.

.service('$AdobeEdge', function() {
  var head = document.getElementsByTagName('head')[0],
    scripts = head.getElementsByTagName('script');

  return { 
    loadComposition: function(projectPrefix, compId, opts, preloaderDOM, downLevelStageDOM) {
      // Determine the filename for our project
      var projectFile = projectPrefix + '_edge.js';
      var newScript = null;

      // Iterate through each script tag in the header to search for our file
      angular.forEach(scripts, function(script, index, scripts) {

        // Does the script src tag end with our project filename?
        if (script.src.substr(script.src.length - projectFile.length) == projectFile) {

          // It's already loaded! Let's go about removing it and injecting a fresh script tag...
          script.remove();
          newScript = document.createElement('script');
          newScript.setAttribute('type', 'text/javascript');
          newScript.setAttribute('src', script.src);
          head.insertBefore(newScript, scripts[0]);

          // Let's also delete the composition within the Adobe Edge API so that events
          // like 'compositionReady' are fired again when we call loadComposition()
          delete AdobeEdge.compositions[compId];
        }
      });

      // Ultimately we always need to call loadComposition() no matter what
      AdobeEdge.loadComposition(projectPrefix, compId, opts, preloaderDOM, downLevelStageDOM);
    }
  }
})

Using this method I can simply invoke this service in the relevant controllers and load a composition in a similar manner to normal. In this particular case...

.controller('GameTest', function($scope, $state, $AdobeEdge) {
  $AdobeEdge.loadComposition('edge-animate/GameTest', 'GameTest', {
    scaleToFit: "width",
    centerStage: "none",
    minW: "0",
    maxW: "undefined",
    width: "2048px",
    height: "1134px"
  }, {dom: [ ]}, {dom: [ ]});
});

So far it's been working great! I'll use it to build out the rest of the games in our project and post any issues I run into.

Hopefully this saves someone else a lot of heartache!

Homogenous answered 23/3, 2015 at 11:54 Comment(4)
Thanks for this well written post (and edits) and please do update with any additional findings as I, and I'm sure others, are doing similar things with EA and AngularJS.Thaler
Excellent post. Would you be willing to share some of your code from your project? I am embarking on the same path as you and would love to skip more of the initial trial-and-error process of merging the two frameworks.Civic
Has this actually worked out? I tried to implement something similar for myself (based on this code), and I still get the TypeErrors ... What I don't understand in your code is why you delete the <script> node and then re-add it right after – shouldn't the call to AdobeEdge.loadComposition() do that for you?Gamy
Awesome find! Added this myself with some modifications as the scripts are loaded inside the body element over here. Thanks!Leisurely
K
2

Answer by Rob Sinton:

The issue seems to be resolved if the browser handles the contents of the relevant *_edge.js again. Since these are injected into the <head> whenever AdobeEdge.loadComposition(...) is called (via yepnope), and there doesn't appear to be any method of asking yepnope to reload injected Javascript, I have written a small service for Angular that I can use to handle this in place of the standard AdobeEdge.loadComposition(...) function. It's essentially a wrapper that will do the relevant checks beforehand.

.service('$AdobeEdge', function() {
  var head = document.getElementsByTagName('head')[0],
    scripts = head.getElementsByTagName('script');

  return { 
    loadComposition: function(projectPrefix, compId, opts, preloaderDOM, downLevelStageDOM) {
      // Determine the filename for our project
      var projectFile = projectPrefix + '_edge.js';
      var newScript = null;

      // Iterate through each script tag in the header to search for our file
      angular.forEach(scripts, function(script, index, scripts) {

        // Does the script src tag end with our project filename?
        if (script.src.substr(script.src.length - projectFile.length) == projectFile) {

          // It's already loaded! Let's go about removing it and injecting a fresh script tag...
          script.remove();
          newScript = document.createElement('script');
          newScript.setAttribute('type', 'text/javascript');
          newScript.setAttribute('src', script.src);
          head.insertBefore(newScript, scripts[0]);

          // Let's also delete the composition within the Adobe Edge API so that events
          // like 'compositionReady' are fired again when we call loadComposition()
          delete AdobeEdge.compositions[compId];
        }
      });

      // Ultimately we always need to call loadComposition() no matter what
      AdobeEdge.loadComposition(projectPrefix, compId, opts, preloaderDOM, downLevelStageDOM);
    }
  }
})

Using this method I can simply invoke this service in the relevant controllers and load a composition in a similar manner to normal. In this particular case...

.controller('GameTest', function($scope, $state, $AdobeEdge) {
  $AdobeEdge.loadComposition('edge-animate/GameTest', 'GameTest', {
    scaleToFit: "width",
    centerStage: "none",
    minW: "0",
    maxW: "undefined",
    width: "2048px",
    height: "1134px"
  }, {dom: [ ]}, {dom: [ ]});
});

So far it's been working great! I'll use it to build out the rest of the games in our project and post any issues I run into.

Hopefully this saves someone else a lot of heartache!

Kilowatt answered 20/1, 2016 at 16:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.