[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:
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user