diff --git a/website_animate/static/src/js/o_animate.editor.js b/website_animate/static/src/js/o_animate.editor.js index 5adf905aa..56ea2b7cc 100644 --- a/website_animate/static/src/js/o_animate.editor.js +++ b/website_animate/static/src/js/o_animate.editor.js @@ -1,27 +1,8 @@ -odoo.define('website_animate.o_animate_editor', function (require) { -'use strict'; +/** @odoo-module **/ -var sOptions = require('web_editor.snippets.options'); +import options from 'web_editor.snippets.options'; -function forceAnimation() { - var $els = $(); - const $scrollingElement = $().getScrollingElement(); - _.each(arguments, function (el) { - $els = $els.add(el); - }); - $els.css('animation-name', 'dummy'); - void $els[0].offsetWidth; - $els.addClass('o_animating'); - $scrollingElement[0].classList.add('o_wanim_overflow_x_hidden'); - $els.css('animation-name', ''); - $els.one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function (e) { - $scrollingElement[0].classList.remove('o_wanim_overflow_x_hidden'); - $els.removeClass('o_animating'); - }); -} - -// Animations -sOptions.registry.o_animate = sOptions.Class.extend({ +options.registry.WebsiteAnimate = options.Class.extend({ //-------------------------------------------------------------------------- // Options @@ -30,12 +11,32 @@ sOptions.registry.o_animate = sOptions.Class.extend({ /** * @override */ - selectClass: async function (previewMode, widgetValue, params) { - await this._super.apply(this, arguments); - forceAnimation(this.$target); + async selectClass(previewMode, widgetValue, params) { + await this._super(...arguments); if (params.isAnimationTypeSelection) { + this._forceAnimation(); this.$target.toggleClass('o_animate_preview o_animate', !!widgetValue); } }, -}); + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _forceAnimation() { + const $scrollingElement = $().getScrollingElement(); + this.$target.css('animation-name', 'dummy'); + // Trigger a DOM reflow. + void this.$target[0].offsetWidth; + this.$target.addClass('o_animating'); + $scrollingElement[0].classList.add('o_wanim_overflow_x_hidden'); + this.$target.css('animation-name', ''); + this.$target.one('webkitAnimationEnd oanimationend msAnimationEnd animationend', () => { + $scrollingElement[0].classList.remove('o_wanim_overflow_x_hidden'); + this.$target.removeClass('o_animating'); + }); + }, }); diff --git a/website_animate/static/src/js/o_animate.frontend.js b/website_animate/static/src/js/o_animate.frontend.js index b87ad1214..362ee24cb 100644 --- a/website_animate/static/src/js/o_animate.frontend.js +++ b/website_animate/static/src/js/o_animate.frontend.js @@ -1,156 +1,49 @@ -odoo.define('website_animate.o_animate_frontend', function (require) { -'use strict'; +/** @odoo-module **/ -var publicWidget = require('web.public.widget'); - -var WebsiteAnimate = { - win : {}, - items : {}, - - offsetRatio : 0.3, // Dynamic offset ratio: 0.3 = (element's height/3) - offsetMin : 10, // Minimum offset for small elements (in pixels) - - // Retrieve animable elements and attach handlers. - start: function () { - var self = this; - self.$scrollingElement = $().getScrollingElement(); - self.items = $(".o_animate"); - self.items.each(function () { - var $el = $(this); - // Set all monitored elements to initial state - self.reset_animation($el); - }); - setTimeout(function () { - self.attach_handlers(); - }); - }, - - // Bind events and define the scrolling function - attach_handlers: function () { - var self = this; - var lastScroll = 0; - - $(window) - .on("resize.o_animate", function () { - self.win.h = $(window).height(); - $(window).trigger("scroll"); - }) - .trigger("resize"); - - self.$scrollingElement - .on("scroll.o_animate, slid.bs.carousel", (_.throttle(function () { - // _.throttle -> Limit the number of times the scroll function - // can be called in a given period. (http://underscorejs.org/#throttle) - var windowTop = $(window).scrollTop(); - var windowBottom = windowTop + self.win.h; - - // Handle reverse scrolling - var direction = (windowTop < lastScroll) ? -1 : 1; - lastScroll = windowTop; - - self.items.each(function () { - var $el = $(this); - var elHeight = $el.height(); - var elOffset = direction * Math.max((elHeight * self.offsetRatio), self.offsetMin); - var state = $el.css("animation-play-state"); - - // We need to offset for the change in position from some animation - // So we get the top value by not taking CSS transforms into calculations - var elTop = self.getElementOffsetTop($el[0]) - $().getScrollingElement().scrollTop(); - - var visible = windowBottom > (elTop + elOffset) && windowTop < (elTop + elHeight - elOffset); - - if ( visible && (state === "paused") ) { - $el.addClass("o_visible"); - self.start_animation($el); - } else if ( !(visible) && $el.hasClass("o_animate_both_scroll") && (state === "running") ) { - $el.removeClass("o_visible"); - self.reset_animation($el); - } - }); - },100))) - .trigger("scroll"); - }, - - // Set elements to initial state - reset_animation: function ($el) { - var self = this; - var anim_name = $el.css("animation-name"); - - $el - .css({"animation-name" : "dummy-none", "animation-play-state" : ""}) - .removeClass("o_animated o_animating"); - - self._toggleOverflowXHidden(false); - - // force the browser to redraw using setTimeout - setTimeout(function () { - $el.css({"animation-name" : anim_name, "animation-play-state" : "paused"}); - },0); - }, - - // Start animation and/or update element's state - start_animation: function ($el) { - var self = this; - // force the browser to redraw using setTimeout - setTimeout(function () { - self._toggleOverflowXHidden(true); - $el - .css({"animation-play-state": "running"}) - .addClass("o_animating") - .one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function (e) { - $el.addClass("o_animated").removeClass("o_animating"); - self._toggleOverflowXHidden(false); - $(window).trigger("resize"); - }); - }); - }, - - // show/hide the horizontal scrollbar (on the #wrapwrap) - _toggleOverflowXHidden: function (add) { - if (add) { - this.$scrollingElement[0].classList.add('o_wanim_overflow_x_hidden'); - } else if (!this.$scrollingElement.find('.o_animating').length) { - this.$scrollingElement[0].classList.remove('o_wanim_overflow_x_hidden'); - } - }, - - // Get element top offset by not taking CSS transforms into calculations - getElementOffsetTop: function (el) { - // Loop through the DOM tree and add its parent's offset to get page offset - var top = 0; - do { - top += el.offsetTop || 0; - el = el.offsetParent; - } while (el); - return top; - }, -}; +import publicWidget from 'web.public.widget'; publicWidget.registry.WebsiteAnimate = publicWidget.Widget.extend({ selector: '#wrapwrap', disabledInEditableMode: false, + offsetRatio: 0.3, // Dynamic offset ratio: 0.3 = (element's height/3) + offsetMin: 10, // Minimum offset for small elements (in pixels) + /** * @override */ - start: function () { + start() { + this.lastScroll = 0; + this.$scrollingElement = $().getScrollingElement(this.ownerDocument); // By default, elements are hidden by the css of o_animate. - // render alements + // We will trigger the animation then pause it in state 0. - WebsiteAnimate.start(); + // Render elements and trigger the animation then pause it in state 0. + this.$animatedElements = this.$target.find('.o_animate'); + _.each(this.$animatedElements, el => { + this._resetAnimation($(el)); + }); // Then we render all the elements, the ones which are invisible // in state 0 (like fade_in for example) will stay invisible. - $(".o_animate").css("visibility", "visible"); + this.$animatedElements.css("visibility", "visible"); - return this._super.apply(this, arguments); + // We use addEventListener instead of jQuery because we need 'capture'. + // Setting capture to true allows to take advantage of event bubbling + // for events that otherwise don’t support it. (e.g. useful when + // scrolling a modal) + this.__onScrollWebsiteAnimate = _.throttle(this._onScrollWebsiteAnimate.bind(this), 200); + this.$scrollingElement[0].addEventListener('scroll', this.__onScrollWebsiteAnimate, {capture: true}); + + $(window).on('resize.o_animate, shown.bs.modal.o_animate, slid.bs.carousel.o_animate', () => { + this.windowsHeight = $(window).height(); + this.$scrollingElement[0].dispatchEvent(new Event('scroll')); + }).trigger("resize"); + + return this._super(...arguments); }, /** * @override */ - destroy: function () { - WebsiteAnimate.$scrollingElement[0].classList.remove('o_wanim_overflow_x_hidden'); - this._super.apply(this, arguments); - + destroy() { + this._super(...arguments); this.$target.find('.o_animate') .removeClass('o_animating o_animated o_animate_preview') .css({ @@ -158,26 +51,112 @@ publicWidget.registry.WebsiteAnimate = publicWidget.Widget.extend({ 'animation-play-state': '', 'visibility': '', }); + $(window).off('.o_animate'); + this.$scrollingElement[0].removeEventListener('scroll', this.__onScrollWebsiteAnimate, {capture: true}); + this.$scrollingElement[0].classList.remove('o_wanim_overflow_x_hidden'); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Starts animation and/or update element's state. + * + * @private + * @param {jQuery} $el + */ + _startAnimation($el) { + // Forces the browser to redraw using setTimeout. + setTimeout(() => { + this._toggleOverflowXHidden(true); + $el + .css({"animation-play-state": "running"}) + .addClass("o_animating") + .one('webkitAnimationEnd oanimationend msAnimationEnd animationend', () => { + $el.addClass("o_animated").removeClass("o_animating"); + this._toggleOverflowXHidden(false); + $(window).trigger("resize"); + }); + }); + }, + /** + * @private + * @param {jQuery} $el + */ + _resetAnimation($el) { + const animationName = $el.css("animation-name"); + $el.css({"animation-name": "dummy-none", "animation-play-state": ""}) + .removeClass("o_animated o_animating"); + + this._toggleOverflowXHidden(false); + // trigger a DOM reflow + void $el[0].offsetWidth; + $el.css({'animation-name': animationName , 'animation-play-state': 'paused'}); + }, + /** + * Shows/hides the horizontal scrollbar (on the #wrapwrap). + * + * @private + * @param {Boolean} add + */ + _toggleOverflowXHidden(add) { + if (add) { + this.$scrollingElement[0].classList.add('o_wanim_overflow_x_hidden'); + } else if (!this.$scrollingElement.find('.o_animating').length) { + this.$scrollingElement[0].classList.remove('o_wanim_overflow_x_hidden'); + } + }, + /** + * Gets element top offset by not taking CSS transforms into calculations. + * + * @private + * @param {Element} el + */ + _getElementOffsetTop(el) { + // Loop through the DOM tree and add its parent's offset to get page offset. + var top = 0; + do { + top += el.offsetTop || 0; + el = el.offsetParent; + } while (el); + return top; + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {Event} ev + */ + _onScrollWebsiteAnimate(ev) { + const scroll = $(ev.currentTarget).scrollTop(); + // Handle reverse scrolling + const direction = (scroll < this.lastScroll) ? -1 : 1; + this.lastScroll = scroll; + + _.each(this.$target.find('.o_animate'), el => { + const $el = $(el); + const elHeight = $el.height(); + const elOffset = direction * Math.max((elHeight * this.offsetRatio), this.offsetMin); + const state = $el.css("animation-play-state"); + + // We need to offset for the change in position from some animation. + // So we get the top value by not taking CSS transforms into calculations. + const $openModal = this.$target.find('.modal:visible'); + const scrollTop = $openModal.length ? $openModal.scrollTop() : this.$scrollingElement.scrollTop(); + const elTop = this._getElementOffsetTop($el[0]) - scrollTop; + + const visible = this.windowsHeight > (elTop + elOffset) && 0 < (elTop + elHeight - elOffset); + if (visible && state === "paused") { + $el.addClass("o_visible"); + this._startAnimation($el); + } else if (!visible && $el.hasClass("o_animate_both_scroll") && state === "running") { + $el.removeClass("o_visible"); + this._resetAnimation($el); + } + }); }, }); - -// Backward compatibility for enark animation system -publicWidget.registry.o_animate = publicWidget.Widget.extend({ - selector: '.o_animation', - - destroy: function () { - this._super.apply(this, arguments); - - // Convert old classes to the new animation system - var old_animation_classes = "o_animation o_displayed o_displayed_top o_displayed_middle o_displayed_bottom o_visible o_visible_top o_visible_middle o_visible_bottom"; - $(".o_fade_in").addClass("o_animate o_anim_fade_in").removeClass("o_fade_in"); - $(".o_fade_in_down").addClass("o_animate o_anim_fade_in_down").removeClass("o_fade_in_down"); - $(".o_fade_in_left").addClass("o_animate o_anim_fade_in_left").removeClass("o_fade_in_left"); - $(".o_fade_in_right").addClass("o_animate o_anim_fade_in_right").removeClass("o_fade_in_right"); - $(".o_fade_in_up").addClass("o_animate o_anim_fade_in_up").removeClass("o_fade_in_up"); - this.$target.removeClass(old_animation_classes); - }, -}); - -return WebsiteAnimate; -}); diff --git a/website_animate/views/options.xml b/website_animate/views/options.xml index 8f8425086..0234c2405 100644 --- a/website_animate/views/options.xml +++ b/website_animate/views/options.xml @@ -19,7 +19,7 @@