[REF] website: refactor parallax snippet

This commit is a refactoring of the 's_parallax' snippet. Note that it
is compatible with previous parallax snippet (old snippets are just
transformed into the brand new one on page load). Note that scroll
speed are changed by this implementation though.

This solves several issues:

- Bg image went easily off of the frame if scroll speed was too fast

- The 'scroll speed' effect was visually inconsistent across different
    devices.

- If the Bg image was smaller than the frame (condition which occurs
    often in a responsive environment), then the image was totally
    offset.

- Since the previous implementation use background-position property
    to perform the effect, It was not possible to define an image
    focus point.

This implementation solves the previous issues introducing a safe-area
that limit the effect range. It's calculated dynamically, according to
the current screen size and the 'speed' option.

The effect if now achieved thanks to the translation of a DOM element
instead of background-position changes. This allows to solve the last
mentionned problem and improve performance.

The implementation rely on the new animation methods of the snippets
Animation class.
As this use the "requestAnimationFrame" function, the snippet knows
the right time to repaint itself, instead of using the "scroll" event
which is triggered too many times and thus occurs between painting
frames. This change greatly improved performances (CPU/painting usage
many times smaller).
This commit is contained in:
stefanorigano
2016-11-22 17:15:33 +01:00
committed by qsm-odoo
parent 0e08e653cd
commit d6cf236b4b
4 changed files with 101 additions and 54 deletions
@@ -314,52 +314,71 @@ animation.registry.slider = animation.Class.extend({
animation.registry.parallax = animation.Class.extend({
selector: ".parallax",
start: function () {
_.defer((function () { this.set_values(); }).bind(this));
$(window).on("scroll.animation_parallax", _.throttle(this.on_scroll.bind(this), 10))
.on("resize.animation_parallax", _.debounce(this.set_values.bind(this), 500));
return this._super.apply(this, arguments);
this._super.apply(this, arguments);
this._rebuild();
$(window).on("resize.animation_parallax", _.debounce(this._rebuild.bind(this), 500));
},
stop: function () {
this._super.apply(this, arguments);
$(window).off(".animation_parallax");
},
_rebuild: function () {
// Add/find bg DOM element to hold the parallax bg (support old v10.0 parallax)
if (!this.$bg || !this.$bg.length) {
this.$bg = this.$("> .s_parallax_bg");
if (!this.$bg.length) {
this.$bg = $("<span/>", {"class": "s_parallax_bg"}).prependTo(this.$target);
}
}
var urlTarget = this.$target.css("background-image");
if (urlTarget !== "none") {
this.$bg.css("background-image", urlTarget);
}
this.$target.css("background-image", "none");
set_values: function () {
var self = this;
this.speed = parseFloat(self.$target.attr("data-scroll-background-ratio") || 0);
this.offset = 0;
// Get parallax speed
this.speed = parseFloat(this.$target.attr("data-scroll-background-ratio") || 0);
if (this.speed === 1 || this.$target.css("background-image") === "none") {
this.$target.css("background-attachment", "fixed").css("background-position", "0px 0px");
// Reset offset if parallax effect will not be performed and leave
this.$target.toggleClass("s_parallax_is_fixed", this.speed === 1);
if (this.speed === 0 || this.speed === 1) {
this.$bg.css({
transform: "",
top: "",
bottom: ""
});
return;
}
this.$target.css("background-attachment", "scroll");
// Initialize parallax data according to snippet and viewport dimensions
this.viewport = document.body.clientHeight - $('#wrapwrap').position().top;
this.visible_area = [this.$target.offset().top];
this.visible_area.push(this.visible_area[0] + this.$target.innerHeight() + this.viewport);
this.ratio = this.speed * (this.viewport / 10);
var img = new Image();
img.onload = function () {
var offset = 0;
var padding = parseInt($(document.body).css("padding-top"));
if (self.speed > 1) {
var inner_offset = - self.$target.outerHeight() + this.height / this.width * document.body.clientWidth;
var outer_offset = self.$target.offset().top - (document.body.clientHeight - self.$target.outerHeight()) - padding;
offset = - outer_offset * self.speed + inner_offset;
} else {
offset = - self.$target.offset().top * self.speed;
}
self.offset = offset > 0 ? 0 : offset;
self.on_scroll();
};
img.src = this.$target.css("background-image").replace(/url\(['"]*|['"]*\)/g, "");
// Provide a "safe-area" to limit parallax
this.$bg.css({
top: -this.ratio,
bottom: -this.ratio
});
},
on_scroll: function (scrollOffset) {
if (this.speed === 0 || this.speed === 1) return;
on_scroll: function () {
if (this.speed === 1) return;
var top = this.offset + window.scrollY * this.speed;
this.$target.css("background-position", "0px " + top + "px");
},
// Perform translate if the element is visible only
var vpEndOffset = scrollOffset + this.viewport;
if (vpEndOffset >= this.visible_area[0] && vpEndOffset <= this.visible_area[1]) {
this.$bg.css("transform", "translateY(" + _getNormalizedPosition.call(this, vpEndOffset) + "px)");
}
function _getNormalizedPosition(pos) {
// Normalize scroll in a 1 to 0 range
var r = (pos - this.visible_area[1]) / (this.visible_area[0] - this.visible_area[1]);
// Normalize accordingly to current options
return Math.round(this.ratio * (2 * r - 1));
}
}
});
animation.registry.share = animation.Class.extend({
@@ -299,34 +299,34 @@ options.registry.parallax = options.Class.extend({
this.grid.size = 8;
return this.grid;
},
on_resize: function () {
this._refresh();
},
_refresh: function () {
this.$target.data("snippet-view").set_values();
},
start: function () {
this._super.apply(this, arguments);
if (!this.$target.data("snippet-view")) {
this.$target.data("snippet-view", new animation.registry.parallax(this.$target));
}
this.scroll();
this._refresh_callback = this._refresh.bind(this);
this.buildingBlock.$el.on("snippet-dropped snippet-activated", this._refresh_callback);
this.$target.on('snippet-option-change snippet-option-preview', this._refresh_callback);
this._toggle_refresh_callback(true);
},
on_focus: function () {
this._super.apply(this, arguments);
this._update_target_to_bg();
},
on_resize: function () {
this._super.apply(this, arguments);
this._refresh();
},
scroll: function (type, value) {
this.$target.attr("data-scroll-background-ratio", value);
this._refresh();
},
set_active: function () {
this._super.apply(this, arguments);
this.$el.find('[data-scroll]').removeClass("active")
.filter('[data-scroll="' + (this.$target.attr('data-scroll-background-ratio') || 0) + '"]').addClass("active");
},
clean_for_save: function () {
this._super.apply(this, arguments);
this.$target.css("background-position", '').css("background-attachment", '');
this._toggle_refresh_callback(false);
},
on_move: function () {
this._super.apply(this, arguments);
@@ -334,8 +334,23 @@ options.registry.parallax = options.Class.extend({
},
on_remove: function () {
this._super.apply(this, arguments);
this.$target.off("snippet-option-change snippet-option-preview", this._refresh_callback);
this.buildingBlock.$el.off("snippet-dropped snippet-activated", this._refresh_callback);
this._toggle_refresh_callback(false);
},
_update_target_to_bg: function () {
this.editor.styles.background.$target = this.$target.data("snippet-view").$bg;
this.editor.styles.background.set_active();
this.editor.styles.background_position.$target = this.$target.data("snippet-view").$bg;
this.editor.styles.background_position.set_active();
},
_refresh: function () {
_.defer((function () {
this.$target.data("snippet-view")._rebuild();
this._update_target_to_bg();
}).bind(this));
},
_toggle_refresh_callback: function (on) {
this.$target[on ? "on" : "off"]("snippet-option-change snippet-option-preview", this._refresh_callback);
this.buildingBlock.$el[on ? "on" : "off"]("snippet-dropped snippet-activated", this._refresh_callback);
},
});
@@ -402,6 +402,17 @@ div.carousel {
margin-bottom: 32px;
}
}
> .s_parallax_bg {
.o-position-absolute(0, 0, 0, 0);
background-size: cover;
background-attachment: scroll;
pointer-events: none;
z-index: -1;
}
&.s_parallax_is_fixed > .s_parallax_bg {
background-attachment: fixed;
}
}
.s_parallax {
+10 -8
View File
@@ -530,13 +530,13 @@
<template id="s_parallax" name="Parallax">
<section class="parallax s_parallax oe_custom_bg oe_img_bg"
style="background-image: url(/web/image/website.s_background_image_04);"
data-scroll-background-ratio="0.3"><div class="oe_structure"/></section>
data-scroll-background-ratio="1.6"><div class="oe_structure"/></section>
</template>
<template id="s_parallax_slider" name="Parallax Slider">
<section class="parallax s_parallax_slider oe_custom_bg oe_img_bg"
style="background-image: url(/web/image/website.s_parallax_slider_default_image);"
data-scroll-background-ratio="0.3">
data-scroll-background-ratio="1.6">
<div><div><div class="oe_structure">
<div id="myQuoteCarousel" class="carousel quotecarousel slide mb0" data-interval="10000">
<!-- Indicators -->
@@ -679,7 +679,7 @@
</div>
<div data-js='background_position'
data-selector="section:not(.parallax), :not(.o_gallery > .container) > .carousel, .s_banner .carousel-content">
data-selector="section, :not(.o_gallery > .container) > .carousel, .s_banner .carousel-content">
<li class="background_position_li" data-background_position="background_position">
<a tabindex="-1" href="#"><i class="fa fa-arrows"/>Background Image Sizing</a>
</li>
@@ -847,12 +847,14 @@
<li class="dropdown-submenu">
<a tabindex="-1" href="#"><i class="fa fa-clock-o"/>Scroll Speed</a>
<ul class="dropdown-menu" name="parallax-scroll">
<li data-scroll="0"><a href="#">Static</a></li>
<li data-scroll="0.3"><a href="#">Very Slow</a></li>
<li data-scroll="0.6"><a href="#">Slow</a></li>
<li data-scroll="0"><a href="#">No-scroll</a></li>
<li class="separator"/>
<li data-scroll="1"><a href="#">Fixed</a></li>
<li data-scroll="1.4"><a href="#">Fast</a></li>
<li data-scroll="1.7"><a href="#">Very Fast</a></li>
<li class="separator"/>
<li data-scroll="0.6"><a href="#">Very Slow</a></li>
<li data-scroll="1.2"><a href="#">Slow</a></li>
<li data-scroll="1.6"><a href="#">Fast</a></li>
<li data-scroll="2"><a href="#">Very Fast</a></li>
</ul>
</li>
</div>