pirony / ks-vue-scrollmagic

Vue plugin that makes using Scrollmagic with Vue a walk in the park
MIT License
45 stars 9 forks source link

How to avoid this plugin in Nuxt 2.0+ #13

Open ghost opened 5 years ago

ghost commented 5 years ago

The module hasn't aged well and seems abandoned by the author, meanwhile GSAP bumped a few major versions and moved much of its infrastructure to ES modules. The following is a simple guide how to use latest GSAP/ScrollMagic as a microplugin in Nuxt 2.0+ (or Vue alone) and avoid the $emit mechanism altogether since a ScrollMagic Controller has all the methods you need otherwise anyway.

npm i --save gsap scrollmagic npm i --save-dev imports-loader

Create a gsap-scrollmagic.js in /plugins

import Vue from 'vue'
import { TweenMax } from 'gsap/TweenMax'
import ScrollMagic from 'scrollmagic'
import SplitText from '../static/SplitText' // any extra plugins
import 'imports-loader?define=>false!scrollmagic/scrollmagic/uncompressed/plugins/animation.gsap'

const GSAPScrollMagic = {
  install (Vue, options) {
    // GSAP
    Vue.prototype.$GSAP = {
      TweenMax,
      TimelineMax,
      Linear,
      Power1,
      Power2,
      Power3,
      Back,
      SplitText
    }
    // ScrollMagic
    Vue.prototype.$ScrollMagic = {
      Controller: new ScrollMagic.Controller(),
      Scene: ScrollMagic.Scene
    }
  }
}

Vue.use(GSAPScrollMagic)

Register the plugin in nuxt.config.js

plugins: [
    { src: '~/plugins/gsap-scrollmagic', ssr: false },
    ...
  ]

Now you can use this.$GSAP.TweenMax() this.$GSAP.TimelineMax() in all components, create ScrollMagic scenes with new this.$ScrollMagic.Scene(), add them to Controller with this.$ScrollMagic.Controller.addScene(scene) and remove them with this.$ScrollMagic.Controller.removeScene(scene) without working around the limitations of saving them as unique strings.

justinriggio commented 5 years ago

@mystrdat I'm getting this error: TypeError: this.$ScrollMagic.Controller is not a constructor

Don't understand what's going on here. Any help?

ghost commented 5 years ago

@justinriggio

Can you share how you're using the Controller in your component?

justinriggio commented 5 years ago

@mystrdat I ended up doing this in the plugin:

// ScrollMagic
    Vue.prototype.$ScrollMagic = {
      Controller: ScrollMagic.Controller,
      Scene: ScrollMagic.Scene
    }

and this in my Nuxt/Vue page:

<script>
...

  var controllerHolder;

  export default {

methods: {

      startFirstAnimationScene() {

      $( "#navMain" ).addClass( "logo-hide" );
      const controller = new this.$ScrollMagic.Controller({globalSceneOptions: {offset: 600}})
      const scene = new this.$ScrollMagic.Scene({triggerElement: "#logoTrigger"})
        .setClassToggle("#navMain", "zippy")
        .addIndicators()
        .addTo(controller);

      controllerHolder = controller;

      }
    },
    destroyed () {
      // Destroy ScrollMagic when our component is removed from DOM
      controllerHolder = controllerHolder.destroy();
    }

...
justinriggio commented 5 years ago

Also got the debug.addIndicators in by:

import 'imports-loader?define=>true!scrollmagic/scrollmagic/uncompressed/plugins/debug.addIndicators'

Maybe this will help others for the debug option.

JakubKoralewski commented 5 years ago

And you can use these TypeScript definitions. (I injected only scrollmagic into the Vue prototype!).

import Vue from "vue";
import { Timeline, Tween } from "gsap";
/* tslint:disable ban-types no-namespace max-classes-per-file */
/* https://github.com/danhanfry/KawasakiReproject/blob/master/KawasakiReproject/scripts/typings/scrollmagic.d.ts */

declare enum SCROLL_DIRECTION {
    SCROLL_DIRECTION_FORWARD,
    SCROLL_DIRECTION_REVERSE,
    SCROLL_DIRECTION_PAUSED
}

declare enum SCENE_STATE {
    SCENE_BEFORE,
    SCENE_DURING,
    SCENE_AFTER
}

interface IControllerOptions {
    container?: string | HTMLElement;
    vertical?: boolean;
    globalSceneOptions?: ISceneOptions;
    loglevel?: number;
    refreshInterval?: number;
}

interface ISceneOptions {
    duration?: number | string | Function;
    offset?: number;
    triggerElement?: string | HTMLElement;
    triggerHook?: number | string;
    reverse?: boolean;
    loglevel?: number;
}

interface ISetPinSettings {
    pushFollowers?: boolean;
    spacerClass?: string;
}

interface ICommonEventProperties {
    type: string;
    target: ScrollMagic.Scene;
}

interface IAddSceneEventProperties extends ICommonEventProperties {
    controller: boolean;
}

interface IChangeScenceEventProperties extends ICommonEventProperties {
    what: string;
    newval: any;
}

interface IDestroySceneEventProperties extends ICommonEventProperties {
    reset: boolean;
}

interface ISceneDirectionEventProperties extends ICommonEventProperties {
    progress: number;
    state: string;
    scrollDirection: string;
}

interface IShiftSceneEventProperties extends ICommonEventProperties {
    reason: string;
}

interface IUpdateSceneEventProperties extends ICommonEventProperties {
    startPos: number;
    endPos: number;
    scrollPos: number;
}

interface ISceneAddIndicatorOptions {
    parent?: string | HTMLElement;
    name?: string;
    indent?: number;
    colorStart?: string;
    colorEnd?: string;
    colorTrigger?: string;
}

interface ISceneAboutProperties {
    size: number;
    vertical: boolean;
    scrollPos: number;
    scrollDirection: string;
    container: HTMLElement;
    isDocument: boolean;
}

declare namespace ScrollMagic {
    export class Controller {
        constructor(options?: IControllerOptions);
        addScene(newScene: Scene | Scene[]): Controller;
        destroy(resetScenes?: boolean): void;
        removeScene(scene: Scene | Scene[]): Controller;
        scrollTo(
            scrollTarget: number | string | HTMLElement | Function | Scene,
            additionalParameter?: any
        ): Controller;
        update(immediately?: boolean): Controller;
        updateScene(scene: Scene, immediately?: boolean): Controller;

        enabled(newState?: boolean): boolean | Controller;
        loglevel(newLogLevel?: number): number | Controller;
        scrollPos(scrollPosMethod?: Function): number | Controller;

        info(
            about?: string
        ): ISceneAboutProperties | number | string | boolean | HTMLElement;
    }

    export class Scene {
        constructor(options?: ISceneOptions);

        addTo(controller: Controller): Scene;
        controller(): Controller;
        destroy(reset?: boolean): void;
        progress(progress?: number): number;
        refresh(): Scene;
        remove(): Scene;
        removeClassToggle(reset?: boolean): Scene;
        removePin(reset?: boolean): Scene;
        setClassToggle(element: string | HTMLElement, classes: string): Scene;
        setPin(
            element: string | HTMLElement,
            settings?: ISetPinSettings
        ): Scene;
        update(immediately?: boolean): Scene;

        duration(newDuration?: number | Function): number;
        enabled(newState?: boolean): boolean | Scene;
        loglevel(newLogLevel?: number): number;
        offset(newOffset?: number): number;
        reverse(newReverse?: boolean): boolean;
        triggerElement(
            newTriggerElement?: string | HTMLElement
        ): string | HTMLElement;
        triggerHook(newTriggerHook?: number | string): number;

        scrollOffset(): number;
        state(): string;
        triggerPosition(): number;

        off(
            names: string,
            callback?: (
                event:
                    | ICommonEventProperties
                    | IAddSceneEventProperties
                    | IChangeScenceEventProperties
                    | IDestroySceneEventProperties
                    | ISceneDirectionEventProperties
                    | IShiftSceneEventProperties
                    | IUpdateSceneEventProperties
            ) => void
        ): Scene;

        on(
            names: string,
            callback: (
                event:
                    | ICommonEventProperties
                    | IAddSceneEventProperties
                    | IChangeScenceEventProperties
                    | IDestroySceneEventProperties
                    | ISceneDirectionEventProperties
                    | IShiftSceneEventProperties
                    | IUpdateSceneEventProperties
            ) => void
        ): Scene;

        trigger(name: string, vars?: any): Scene;

        /* add indicator plugins */
        addIndicators(options?: ISceneAddIndicatorOptions): Scene;
        removeIndicators(): void;

        /*GSAP plugins */
        removeTween(reset?: boolean): Scene;
        setTween(tweenObject: Timeline | Tween): Scene;
        setTween(
            tweenObject: string | HTMLElement | Tween,
            duration: number,
            params: Object
        ): Scene;
        tweenChanges(newTweenChanges?: boolean): Scene;
    }
}

declare module "vue/types/vue" {
    interface Vue {
        $ScrollMagic: {
            Controller: ScrollMagic.Controller;
            Scene: new (options: ISceneOptions) => ScrollMagic.Scene;
        };
    }
}
mvaneijgen commented 5 years ago

Any tips on how to use new this.$GSAP.TimelineMax() within the data() object?

When I do the following I get the error Cannot read property 'TimelineMax' of undefined. (I've also tried running vm.$GSAP..., $nuxt.$GSAP..., $GSAP..., but it doens't seem to work within data().

...
  data() {
    return {
      tlMorph: new this.$GSAP.TimelineMax(),
      played: false,
    };
  },
...

I want to create a timeline that is consistent through several methods. If I create it in the method I can't run tlMorph.reverse() on it, because it keeps creating a new timeline on each run.

fvonellerts commented 5 years ago

@mvaneijgen You could define the timeline as this.tlMorph inside the mounted() hook, the rest of your code should work the same.

seanmart commented 5 years ago

I'm getting this error: This dependency was not found:

package.json: "dependencies": { "@nuxtjs/style-resources": "^1.0.0", "gsap": "^3.0.1", "lodash": "^4.17.15", "nuxt": "^2.0.0", "scrollmagic": "^2.0.7" }, "devDependencies": { "node-sass": "^4.12.0", "sass-loader": "^8.0.0" "imports-loader": "^0.8.0", }

ghost commented 5 years ago

GSAP 3 is a major rewrite and doesn't have the individual TweenMax/TimelineMax objects anymore afaik.

On Sun, Nov 24, 2019, 21:40 seanmart notifications@github.com wrote:

I'm getting this error: This dependency was not found:

  • gsap/TweenMax in ./plugins/gsap-scrollmagic.js

package.json: "dependencies": { "@nuxtjs/style-resources": "^1.0.0", "gsap": "^3.0.1", "lodash": "^4.17.15", "nuxt": "^2.0.0", "scrollmagic": "^2.0.7" }, "devDependencies": { "node-sass": "^4.12.0", "sass-loader": "^8.0.0" "imports-loader": "^0.8.0", }

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/pirony/ks-vue-scrollmagic/issues/13?email_source=notifications&email_token=AAMTRHYNBFFOX7H3MA6LBUDQVLRFVA5CNFSM4GI3CYX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEFAULSQ#issuecomment-557925834, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAMTRH3BNWHB55PVZKM4PYLQVLRFVANCNFSM4GI3CYXQ .

mikehoh commented 4 years ago

Hello! @justinriggio Thanks or your example. Didn't you have any errors related to imports-loader?

With this import import 'imports-loader?define=>false!scrollmagic/scrollmagic/uncompressed/plugins/animation.gsap'; I have such error:

 ERROR  in ./node_modules/scrollmagic/scrollmagic/uncompressed/plugins/animation.gsap.js                                                                                    friendly-errors 09:47:09

Module build failed (from ./node_modules/imports-loader/dist/cjs.js):                                                                                                       friendly-errors 09:47:09
ValidationError: Invalid options object. Imports Loader has been initialized using an options object that does not match the API schema.
 - options should be one of these:
   object { imports, … } | object { wrapper, … } | object { additionalCode, … }
   Details:
    * options has an unknown property 'define'. These properties are valid:
      object { imports, … } | object { wrapper, … } | object { additionalCode, … }
    * options misses the property 'imports' | should be any non-object.
    * options misses the property 'wrapper' | should be any non-object.
    * options misses the property 'additionalCode' | should be any non-object.
cmg193 commented 2 years ago

@mvaneijgen You could initialize it before the return (data is a function...), or inside the mounted function.

Any tips on how to use new this.$GSAP.TimelineMax() within the data() object?

When I do the following I get the error Cannot read property 'TimelineMax' of undefined. (I've also tried running vm.$GSAP..., $nuxt.$GSAP..., $GSAP..., but it doens't seem to work within data().

...
  data() {
    return {
      tlMorph: new this.$GSAP.TimelineMax(),
      played: false,
    };
  },
...

I want to create a timeline that is consistent through several methods. If I create it in the method I can't run tlMorph.reverse() on it, because it keeps creating a new timeline on each run.