Jump to content


  • Posts

  • Joined

  • Last visited

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

teejay_hh's Achievements



  1. Wow those 2 tweak made this run without any studder at 60 frames, thanks so much. I used will-change in the past but I couldn't remember it anymore.
  2. I replaced the - quickSetter with normal to tweens -> gsap.to(this.hills, {duration: 0.1, x: s/5 }) gsap.to(this.clouds, {duration: 0.1, x: s/8 }) which seems to make a big difference. Not perfect but closer to what I want.
  3. Hello I have a question performance. I wrote small horizontal slider which also moves 2 background images. The following code shows my slider implementation. I wrote this for my team so there are few more comments which dont assume gsap knowledge. define(function (require) { const gsap = require('gsap/gsap.min').gsap const Draggable = require('gsap/Draggable.min').Draggable const InertiaPlugin = require('gsap/InertiaPlugin.min').InertiaPlugin gsap.registerPlugin(Draggable, InertiaPlugin) /** * The slider class creates a draggable slider which uses gsap's draggable plugin * The slider requires a certain dom structure to function and therefore is not reusable * The class requires `gsap` as wells as the 2 plugins `Draggable, InertiaPlugin` to be registered * * The draggable will start an auto rotation by default, which can be interrupted via interacting with it. * * @class Slider * * * */ return class Slider { constructor(delay = 8, endCallback = () => {}) { this.slideDelay = delay this.slideDuration = 1 this.slider = document.getElementById('slider') this.slides = document.querySelectorAll('.slide') this.slidesInner = document.querySelectorAll('.slides-inner') this.grass = document.getElementById('bg-three') this.hills = document.getElementById('bg-two') this.clouds = document.getElementById('bg-one') this.numSlides = this.slides.length this.snapX = 0 this.animationCounter = 1 this.slideWidth = this.slides[0].offsetWidth this.endCallback = endCallback this.hillsXSetter = gsap.quickSetter(this.hills, 'css') this.cloudsXSetter = gsap.quickSetter(this.clouds, 'css') this.createResizeListener() this.createDraggable() this.initiateSliderAnimation() } /** * Creates the draggable component with a few defaults. Please check out the * documentation if you would like to know more about what this plugin can do * * @see https://greensock.com/docs/v3/Plugins/Draggable */ createDraggable() { this.draggable = new Draggable(this.slidesInner, { type: 'x', // only horizontal scrolling bounds: this.slider, // bounds the draggable to the container so it cant be dragged out of it inertia: true, // momentum - throwing behaviour (magic) edgeResistance: 0.8, dragResistance: 0.3, throwProps: true, onDragStart: this.killAnimation, // callback which gets executed on every tick of dragging onDrag: this.onDrag, // callback which gets executed on every tick of dragging onThrowUpdate: this.onDrag, // callback which gets executed when the draggable is thrown snap: this.snapX, // is set to a function which calculates a snapping position callbackScope: this, // sets the callback scope to be the class instead of the draggable instance // implement some cool live snapping }) } /** * We start a delayed call which initiated the animation cycle to run through all slides until the end * This will set a timer variable which can be used to either kill the call or to restart it. */ initiateSliderAnimation() { this.timer = gsap.delayedCall(this.slideDelay, this.autoPlay, null, this) } /** * Stop the animation loop */ killAnimation() { if (this.timer) this.timer.kill() } /** * The method get called by the delayed call `initiateSliderAnimation` which starts the slide animation * When the draggable is pressed, dragged or thrown at the time of execution then we will kill the next call. * We also stop at the end of the last slide which executes a callback which is used to redirect to a different page. */ autoPlay() { if (this.animationCounter >= this.numSlides) { this.killAnimation() this.endCallback() return } if (this.draggable.isPressed || this.draggable.isDragging || this.draggable.isThrowing) { this.killAnimation() } else { this.animateSlides() } } /** * The animateSlides method runs an animation which slides the draggable one screen further * If the function is called multiple times it will progress through all slides sequentially * We use the timer variable to create a loop which calls the delayed call from `initiateSliderAnimation` method * again. */ animateSlides() { const _this = this // callback functions require us to use a scope var _this.animationCounter++ _this.timer.restart(true) // create a loop // slides the draggable one screen further gsap.to(_this.slidesInner, { x: '-=' + _this.slideWidth, duration: _this.slideDuration, ease: 'quad.inOut', onUpdate: function (s) { const drag = _this.draggable.update() _this.onDrag(null, drag.x) }, }) } /** * This method is called when the we animate or drag the draggable. Its used to create the paralax effect * on the different backgrounds. * * @param pointer * @param s */ onDrag(pointer = null, s = null) { if (s === null) s = this.draggable.x // quicksetter (defined in the constructor) have a massive performance boost // to the normal set method at least 50% - https://greensock.com/docs/v3/GSAP/gsap.quickSetter() this.hillsXSetter({ x: s / 5 }) this.cloudsXSetter({ x: s / 8 }) } /** * The resizeListener listens to the window resize event and sets the Draggable width programmatically */ createResizeListener() { window.addEventListener('resize', this.setDraggableWidth.bind(this)) this.setDraggableWidth() } /** * We use the window width set the slide width to stretch the whole screen * The slides are setup as flex colums of 1, which means they will adjust to the size of its parent container. * To make these slides fullscreen we need to set the container the size of x times the screen width. * We also adjust the snaping based on the current screen width. */ setDraggableWidth() { this.slideWidth = document.documentElement.clientWidth || document.body.clientWidth gsap.set(this.slidesInner, { width: this.numSlides * this.slideWidth + 'px' }) gsap.set(this.slides, { width: 100 / this.numSlides + '%' }) // make sure we show the inner container after the slides are repositioned. gsap.to(this.slidesInner, { delay: 0.5, duration: 0.5, autoAlpha: 1 }) this.snapX = function (endValue) { const slideWidth = document.body.offsetWidth return Math.round(endValue / slideWidth) * slideWidth } if (this.draggable) this.draggable.update(true) } } }) Its fairly straight forward and pretty pretty fast on my mbpro but thats not the measure. (I am not sure I can create a pen with the business plugins and I would have to change all assets (I guess placeholder would work)) Unfortunately this is not running smooth (like 60fps smooth) on an ipad of the 7th gen which I consider pretty fast (A10 Fusion chip). When I take out the dragging x setter (no paralax effect) everything is really nice, but moving the images is the problem. What steps can I take to optimize it? Perhaps: - dont resize images, keep them in their original size (no with 100% stretch)? - try different way of setting the x pos on those images ? - reduce size of images (remove transparent portion) - I have implemented the paralax effect wrong ? Please let me know if you have any tips. Cheers Thomas
  4. Hello, I am wondering if there is a nice and simple way to set all tweens duration of a timeline to 0 so it does not animate anything but instead applies the props instantly and other timelines. Essentially go to the end without animation anything. I created a codepen which uses seek to do this sort of thing, would this be the right approach or is there something more elegant?
  5. Thx, mikel but I really prefer my solution as it does exactly what I expect scrollto to do in first place. Adding additional space to the bottom of a scrollable area is not great, imagine this on a website with a fixed header its simply not working like that. I really appreciate your input, but you suggested exactly the one thing, I mentioned in my first post , that didn’t want to do. Have a great weekend Thomas
  6. Hello mikel, your suggested solution is simply not working in a dynamic context, which I didn’t make quite clear, the search input field could have given it away though. This is why this can’t be a pure css solution. So I had to find a difference solution. And the more I think about it the more I think that the offsetY behaviour is buggy in that regard, but it might not be trivial to fix. Anyway the solution above is workable and easy enough to implement. Cheers everyone. Have great weekend... soon.
  7. For that I would have to measure the ul for its height. Adding a margin-bottom to the ul of the 'offsetY' value would do the trick, but its uggly
  8. thx for the suggestions, I dont really want to set height or margin on any element. For this case it really only affects the last entry. I might be able to get away the `max` solution for the last entry { y: 'max', offsetY: 0 } I kind of still think scrollTo should have the smarts to do this by itself. The same happens for fixed elements and window scroll btw. Try scrolling to the last item
  9. Hi, I have build a really small component (see codepen) which is a list of entries with the first being sticky element. I would like to scroll to an entry which seem slightly buggy due to the sticky element, or I am doing something wrong. If you click on the the last element you will see what the problem is. It works well for entries where scrolling is required, but if en entry sits at the end (entry 9 for instance), where we can't scroll to, then the scroller does not scroll to very end but leaves the gap at the bottom (offset of 30px). Is there a way around this (without adding a margin-bottom to the ul list). I hope I could explain it well, if not, please let me know and will try to elaborate. Best regards Thomas PS: the markup can't be changed
  10. Totally, I am happy to share. Love gsap, keep up the amazing work.
  11. Ok I got something. I created a shim which created the wiggle namespace. /* global define, self */ (function() { var plugins = { 'CustomEase': CustomEase, 'CustomWiggle': CustomWiggle, }; define('wiggle', [], function() { 'use strict'; return plugins; }); })(); Then you can add the the imports to the ember-cli-build.js file app.import('vendor/CustomEase.min.js') app.import('vendor/CustomWiggle.min.js') app.import('vendor/gsapShim.js', { exports: { 'wiggle': ['CustomWiggle', 'CustomEase'] } }) And then you can use it like this in your components. import { CustomWiggle } from 'wiggle'
  12. window.com.greensock.easing.CustomWiggle.create('myWiggle', { wiggles: 6 }) this works, but its really not a fantastic way to do this in ember. I will keep dabbling and see if I can get it to work in more emberish way. Thx a lot for your help.
  13. That package is a community addon which makes it pretty easy to add just TimelineMax or specific easting to the ember build. I have not tried it without the addon, which would still leave me with what would I need to import to actually use Tweenmax for instance. I tried this https://guides.emberjs.com/v2.13.0/addons-and-dependencies/managing-dependencies/#toc_amd-javascript-modules app.import('vendor/CustomWiggle.min.js', { exports: { wiggle: ['CustomWiggle'] } }) and then import { CustomWiggle } from 'wiggle' But no luck.