X-Project Components - is a collection of the most useful components for page layout. For each component are presented description and code examples with the help of which you can easily understand and apply the right component on your website. In X-Project Components all css code was written by SASS (with SCSS syntax) and all javascript code with the use of a library jQuery.
All css code for components was written using BEM naming, meaning .block-name for independent block, .block-name__element-name for elements inside that block. And .-modifier-name for modifiers of the block. For javascript hooks was used prefix .js-*.
Every component are checked for validity through validator.w3.org, tested for сross-browser compatibility through browserstack.com, and works in the latest versions of all popular browsers (including IE11).
1) To start working with the X-Project Components you must to include jQuery library before your javascript code:
<!-- jQuery library -->
<script src="/path/to/jquery.js"></script>
<!-- Your JS file -->
<script src="/path/to/common.js"></script>
3) Find the right component and copy it to your project.
Changes | Date |
---|---|
A little refactoring of all components. Added Hamburger menu, Viewport units on mobile and Background video components. |
24.03.2021 |
Refactoring of all components. |
17.10.2019 |
Added SVG animation component. |
25.07.2018 |
Fixed Count Up and Progress bars components (for the count up numbers used countUp.js), added HTML map marker component. |
17.06.2018 |
Added count up component, added function for scroll bar compensation, fixed decimal converting in progress bars |
20.05.2018 |
Added progress bars component |
09.05.2018 |
Added shuffle filter component |
01.05.2018 |
Added preloader component |
15.04.2018 |
Adding a preloader to the site with few lines of jQuery code. It displays a loading animation until the browser fetched the whole webcontent and will fade out the moment the page has been completely loaded. It is recommended to insert this code first, before all other user scripts.
$preloader-overlay-bg-color: #FFFFFF;
.preloader-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background: $preloader-overlay-bg-color;
z-index: 9999;
&__preloader-item {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
// styles for preloader item
.preloader-item {
height: 50px;
width: 50px;
border: 5px solid rgba(#000000, 0.3);
border-top: 5px solid #000000;
border-radius: 50%;
animation: rotate 1s infinite linear;
}
@keyframes rotate {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
}
// PRELOADER
function initPreloader() {
var preloader = $('.js-preloader'),
hideTimeout = 1000;
// hide preloader by click
preloader.on('click', function() {
$(this).fadeOut('slow');
});
$(window).on('load', function() {
// hide preloader
setTimeout(function() {
preloader.fadeOut('slow');
}, hideTimeout);
});
}
initPreloader();
Say you have page that has a bunch of transitions and animations on all sorts of elements. Some of them get triggered when the window is resized because they have to do with size of the page or position or padding or something. It doesn’t really matter what it is, the fact that the transition or animation runs may contribute to a feeling of jankiness as you resize the window. If those transitions or animations don’t deliver any benefit in those scenarios, you can turn them off!
.resize-animation-stopper * {
animation: none !important;
transition: none !important;
}
// STOP ANIMATIONS DURING WINDOW RESIZING
function initStopAnimationsDuringWindowResizing() {
var resizeTimer;
$(window).on('resize', function() {
$('body').addClass('resize-animation-stopper');
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function() {
$('body').removeClass('resize-animation-stopper');
}, 400);
});
}
initStopAnimationsDuringWindowResizing();
// STOP ANIMATIONS DURING WINDOW RESIZING
let initStopAnimationsDuringWindowResizing = () => {
let resizeTimer;
window.addEventListener('resize', () => {
document.body.classList.add('resize-animation-stopper');
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
document.body.classList.remove('resize-animation-stopper');
}, 400);
});
}
initStopAnimationsDuringWindowResizing();
Quick way to set an HTML text input to only allow numeric keystrokes
// ONLY NUMERIC INPUT
function initOnlyNumericInput() {
$('.js-only-numeric-input').on('input', function() {
this.value = this.value.replace(/[^\d]/g, '');
});
}
initOnlyNumericInput();
// ONLY NUMERIC INPUT
let initOnlyNumericInput = () => {
let inputElement = document.getElementsByClassName('js-only-numeric-input')[0];
if (typeof(inputElement) != 'undefined' && inputElement != null) {
inputElement.addEventListener('input', function() {
this.value = this.value.replace(/[^\d]/g, '');
});
}
}
initOnlyNumericInput();
The trick to viewport units on mobile.
.menu {
height: 100vh; /* Fallback for browsers that do not support Custom Properties */
height: calc(var(--vh, 1vh) * 100);
}
// VIEWPORT UNITS ON MOBILE
function initViewportUnitsOnMobile() {
var vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', vh + 'px');
$(window).on('resize', function() {
vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', vh + 'px');
});
}
initViewportUnitsOnMobile();
Animating SVG paths to animate the drawing sequence. The animation triggered as soon as you have scrolled to the element.
This component is dependent on vivus.js, and waypoints.js.
Icons should be specially prepared (ensure there are no fills).
<script src="/path/to/vivus.js"></script>
<script src="/path/to/waypoints.js"></script>
<script src="/path/to/common.js"></script>
.svg-animation-item {
fill: none;
path {
stroke-width: 4;
stroke: transparent;
}
&.-animation-init {
path {
stroke: inherit;
}
}
// COLORS
&.-purple {
stroke: #4C4E6D;
}
}
// SVG ANIMATION
function initSvgAnimation() {
function svgAnimation(svgItem, duration) {
$(svgItem).addClass('-animation-init');
new Vivus(svgItem, {
duration: duration,
type: 'oneByOne'
});
}
function svgAnimationOnScroll() {
var svgItem = $('.js-svg-animation-item');
if (svgItem.length) {
svgItem.waypoint({
handler: function() {
if (!this.element.svgAnimationInit) {
this.element.svgAnimationInit = true;
svgAnimation(this.element, 300)
}
},
offset: '80%'
})
}
}
svgAnimationOnScroll();
}
initSvgAnimation();
Animates a number from zero to end value, which is triggered when you scroll to an target element. In example for a grid is used Bootstrap.
0%
0%
0%
This component is dependent on countUp.umd.js, and waypoints.js.
<script src="/path/to/countUp.umd.js"></script>
<script src="/path/to/waypoints.js"></script>
<script src="/path/to/common.js"></script>
// STYLES FOR AN EXAMPLE - REMOVE/EDIT AFTER COPYING
.count-up-item {
font-size: 40px;
margin-bottom: 0;
text-align: center;
}
// COUNT UP
function initCountUp() {
function countUpNumbers(countUpItem, duration) {
var countNumber = $(countUpItem).attr('data-count-number'),
numbersAfterComma = 0;
if (countNumber.indexOf('.') > 0) {
numbersAfterComma = countNumber.length - (countNumber.indexOf('.') + 1);
} else if (countNumber.indexOf(',') > 0) {
numbersAfterComma = countNumber.length - (countNumber.indexOf(',') + 1);
}
var options = {
startVal: 0,
decimalPlaces: numbersAfterComma,
duration: duration/1000,
useEasing: true,
decimal: '.',
suffix: '%'
};
new countUp.CountUp(countUpItem, countNumber, options).start();
}
function countUpNumbersOnScroll() {
var countUpItem = $('.js-count-up-item');
if (countUpItem.length) {
countUpItem.waypoint({
handler: function() {
if (!this.element.countUpInit) {
this.element.countUpInit = true;
countUpNumbers(this.element, 8000);
}
},
offset: '80%'
})
}
}
countUpNumbersOnScroll();
}
initCountUp();
Сreating animated progress bars, which triggered as soon as you scrolling to an element.
This component is dependent on countUp.umd.js, and waypoints.js.
<script src="/path/to/countUp.umd.js"></script>
<script src="/path/to/waypoints.js"></script>
<script src="/path/to/common.js"></script>
.progress-bar-item {
display: flex;
align-items: center;
&__title {
flex-basis: 120px;
padding-right: 15px;
margin-bottom: 0;
line-height: 1;
}
&__strip-wrap {
width: 100%;
overflow: hidden;
flex-basis: 0;
flex-grow: 1;
}
}
.progress-bar-strip {
position: relative;
width: 0;
height: 20px;
background-color: #4C4E6C;
&__percent {
position: absolute;
top: 50%;
right: 5px;
transform: translate(0, -50%);
color: #fff;
white-space: nowrap;
}
}
.progress-bars-wrap {
&__item {
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
}
// PROGRESS BARS
function initProgressBars() {
function progressBars(progressBarsBlock, duration) {
var progressBarItem = $(progressBarsBlock).find('.js-progress-bar-item');
progressBarItem.each(function() {
var progressBarsFillItem = $(this).find('.js-progress-bar-strip'),
progressBarsCountUpItem = $(this).find('.js-progress-bar-percent'),
progressBarPercent = progressBarsCountUpItem.attr('data-progress-percent');
// progress bar fill animation
progressBarsFillItem.animate({
width: progressBarPercent + '%'
}, duration, 'swing');
// find amount of numbers after the decimal point
var numbersAfterComma = 0;
if (progressBarPercent.indexOf('.') > 0) {
numbersAfterComma = progressBarPercent.length - (progressBarPercent.indexOf('.') + 1);
}
// count up animation
var options = {
useEasing: false,
decimal: '.',
decimalPlaces: numbersAfterComma,
duration: duration/1000,
suffix: '%'
};
new countUp.CountUp(progressBarsCountUpItem.get(0), progressBarPercent, options).start();
});
}
function progressBarsOnScroll() {
var progressBarsBlock = $('.js-progress-bars');
if (progressBarsBlock.length) {
progressBarsBlock.waypoint({
handler: function() {
if (!this.element.progressBarsInit) {
this.element.progressBarsInit = true;
progressBars(this.element, 4000);
}
},
offset: '80%'
})
}
}
progressBarsOnScroll();
}
initProgressBars();
Simple and beautiful filtration of items based on shuffle.js. In example for a grid is used Bootstrap.
This component is dependent on shuffle.js.
In order to support browsers like IE11 and Safari 8, you must include a babel-polyfil before all your compiled Babel code. Polyfill you can get here.
<script src="/path/to/polyfill.min.js"></script>
<script src="/path/to/shuffle.min.js"></script>
<script src="/path/to/common.js"></script>
// STYLES FOR AN EXAMPLE - REMOVE/EDIT AFTER COPYING
.filter-nav {
text-align: center;
margin-bottom: 30px;
&__item {
display: inline-block;
margin: 0 10px;
}
&__link {
font-size: 1.25rem;
font-weight: 700;
color: #25253E;
opacity: 0.5;
&.-active {
opacity: 1;
}
}
}
.filter-item {
height: 150px;
border-radius: 5px;
display: -webkit-flex;
display: -moz-flex;
display: -ms-flex;
display: -o-flex;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
color: #fff;
margin-bottom: 1rem;
p {
margin-bottom: 0;
}
&.-red {
background-color: #d04343;
}
&.-green {
background-color: #48a748;
}
&.-blue {
background-color: #3b46bd;
}
}
// SHUFFLE FILTER
function initFilter() {
var filterContainer = $('.js-filter-container');
if (filterContainer.length) {
var filterContent = filterContainer.find('.filter-content'),
filterNav = filterContainer.find('.filter-nav'),
filterCategoryName = '',
shuffle = window.shuffle;
var myShuffle = new Shuffle(filterContent, {
speed: 400,
easing: 'ease',
});
myShuffle.update();
// filtering by click
filterNav.find('a').on('click', function() {
filterNav.find('a').removeClass('-active');
$(this).addClass('-active');
filterCategoryName = $(this).attr('data-group');
myShuffle.filter(filterCategoryName, shuffle);
myShuffle.update();
});
}
}
initFilter();
Adding class="-active" after the start of scrolling and removing it, when you are at the top of the page. With this class you can make the header smaller or fill it with color.
.header {
&.-active {
// small header styles
}
}
// ACTIVE HEADER AFTER SCROLL
function initActiveHeaderAfterScroll() {
var header = $('.js-header');
$(window).on('scroll', function() {
if ($(this).scrollTop() > 10) {
header.addClass('-active');
} else {
header.removeClass('-active');
}
});
if ($(document).scrollTop() > 10) {
header.addClass('-active');
}
}
initActiveHeaderAfterScroll();
Simple button for scrolling to top of the page. Button is hide, when you are in the top of page, and visible when you start scrolling.
For icon were used Font Awesome 5.
$scroll-top-btn-size: 50px;
$scroll-top-btn-bg: #26263F;
.scroll-top-btn {
position: fixed;
bottom: 0;
right: 50px;
z-index: 101;
width: $scroll-top-btn-size;
height: $scroll-top-btn-size;
border: none;
cursor: pointer;
background-color: $scroll-top-btn-bg;
transition: 0.3s;
transform: translate(0, 100%);
&__icon {
font-size: 35px;
color: #fff;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
&:hover {
background-color: lighten($scroll-top-btn-bg, 5%);
}
&.-show {
transform: translate(0, 0);
}
}
function initScrollTopBtn() {
function showHideScrollTopBtn(distance) {
var scrollTopBtn = $('.js-scroll-top-btn');
$(window).on('scroll', function() {
if ($(this).scrollTop() > distance) {
scrollTopBtn.addClass('-show');
} else {
scrollTopBtn.removeClass('-show');
}
});
if ($(document).scrollTop() > distance) scrollTopBtn.addClass('-show');
}
function scrollTopAnimation() {
var scrollTopBtn = $('.js-scroll-top-btn'),
scrollingComplete = true;
scrollTopBtn.on('click', function() {
if (scrollingComplete) {
scrollingComplete = false;
$('body, html').animate({
scrollTop: 0
}, 1000 ).promise().done(function() {
scrollingComplete = true;
});
return false;
}
});
}
function scrollTopBtn() {
// 1) checking the distance from the top of the page
showHideScrollTopBtn(100);
// 2) сlick event to scroll top
scrollTopAnimation();
}
scrollTopBtn();
}
initScrollTopBtn();
Simple scrolling to element. Give to target element id="target", and to link href="#target".
If you have a link with an attribute href that begins with a character #, and you do not want to repeat the behavior of the smooth scroll to the element, just add class="js-no-scroll" to this element.
In order to compensate for the height of a fixed header, just add class="js-header" to header.
// SMOOTH ANCHOR LINKS
function initSmoothAnchorLinks() {
var animationComplete = true;
$('a[href^="#"]:not([href="#"]):not(.js-no-scroll)').on('click', function(e) {
e.preventDefault();
// height of header (for offset)
var headerOffset = $('.js-header').outerHeight(),
idOfElement = $(this).attr('href');
if (headerOffset === undefined) {
headerOffset = 0;
}
var top = $(idOfElement).offset().top - headerOffset;
if (animationComplete) {
animationComplete = false;
$('body, html').animate({
scrollTop: top
}, 1000 ).promise().done(function() {
animationComplete = true;
});
}
});
}
initSmoothAnchorLinks();
Cross-browser method of getting scroll bar width via JavaScript. This can be useful, for example, when you open pop-up, and turn off the page scrolling (scrollbar disappears and happening jump of the page). In this case, you will need to compensate the width of the scrollbar.
// WIDTH OF SCROLLBAR
function getScrollBarWidth() {
if (window.innerWidth > $(window).width()) {
var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
$outer.remove();
window.widthOfScrollbar = 100 - widthWithScroll;
return 100 - widthWithScroll;
} else {
return window.widthOfScrollbar = 0;
}
}
getScrollBarWidth();
$(window).on('resize', function() {
getScrollBarWidth();
});
function addScrollbarCompensation(element) {
element.css('padding-right', window.widthOfScrollbar);
}
function removeScrollbarCompensation(element) {
element.css('padding-right', 0);
}
// example
addScrollbarCompensation($('.element-one, .element-two'));
removeScrollbarCompensation($('.element-one, .element-two'));
Creation and customize custom сheckboxs and radio buttons only using HTML and CSS.
For icon were used Font Awesome 5.
// CUSTOM CHECKBOX
$checkbox-checkmark-size: 25px;
$checkbox-bg-color: #4C4E6C;
// states
$checkbox-checked-bg-color: #26263F;
$checkbox-disabled-bg-color: #a2a3b4;
$checkbox-focus-box-shadow-color: #c0c774;
.custom-checkbox {
position: relative;
padding-left: 35px;
margin-bottom: 15px;
line-height: $checkbox-checkmark-size;
cursor: pointer;
&__checkmark {
position: absolute;
top: 50%;
left: 0;
transform: translate(0, -50%);
width: $checkbox-checkmark-size;
height: $checkbox-checkmark-size;
background-color: $checkbox-bg-color;
box-shadow: 0 0 0 0 $checkbox-focus-box-shadow-color;
transition: background-color 0.3s, box-shadow 0.3s;
}
&__icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: inline-block;
font-size: 15px;
color: #fff;
opacity: 0;
transition: opacity 0.2s;
}
&__input {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
clip: rect(0 0 0 0);
overflow: hidden;
// checked styles
&:checked + {
.custom-checkbox__checkmark {
background-color: $checkbox-checked-bg-color;
.custom-checkbox__icon {
opacity: 1;
}
}
}
// disabled styles
&:disabled + {
.custom-checkbox__checkmark {
background-color: $checkbox-disabled-bg-color;
}
}
// focus styles
&:focus + {
.custom-checkbox__checkmark {
box-shadow: 0 0 0 3px $checkbox-focus-box-shadow-color;
}
}
}
&:hover {
.custom-checkbox__input:not(:disabled):not(:checked) + {
.custom-checkbox__checkmark {
background-color: lighten($checkbox-bg-color, 10%);
}
}
}
}
// CUSTOM RADIO
$radio-checkmark-size: 25px;
$radio-bg-color: #4C4E6C;
// states
$radio-checked-bg-color: #26263F;
$radio-disabled-bg-color: #a2a3b4;
$radio-focus-box-shadow-color: #c0c774;
.custom-radio {
position: relative;
padding-left: 35px;
margin-bottom: 15px;
line-height: $radio-checkmark-size;
cursor: pointer;
&__checkmark {
position: absolute;
top: 50%;
left: 0;
transform: translate(0, -50%);
width: $radio-checkmark-size;
height: $radio-checkmark-size;
border-radius: 50%;
background-color: $radio-bg-color;
box-shadow: 0 0 0 0 $checkbox-focus-box-shadow-color;
transition: background-color 0.3s, box-shadow 0.3s;
}
&__icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: inline-block;
font-size: 9px;
color: #ffffff;
transition: opacity 0.2s;
opacity: 0;
}
&__input {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
clip: rect(0 0 0 0);
overflow: hidden;
// checked styles
&:checked + {
.custom-radio__checkmark {
background-color: $radio-checked-bg-color;
.custom-radio__icon {
opacity: 1;
}
}
}
// disabled styles
&:disabled + {
.custom-radio__checkmark {
background-color: $radio-disabled-bg-color;
}
}
// focus styles
&:focus + {
.custom-radio__checkmark {
box-shadow: 0 0 0 3px $checkbox-focus-box-shadow-color;
}
}
}
&:hover {
.custom-radio__input:not(:disabled):not(:checked) + {
.custom-radio__checkmark {
background-color: lighten($radio-bg-color, 10%);
}
}
}
}
Embed a Google Map on site. The following settings for google map are also prepared in advance: styles for theme, custom icon (png or svg), info window and others.
For map with multiple markers zoom are calculated automatically based on the distance of the markers from each other.
Add class="-map-absolute" to make the map stretched across his parent's area.
First of all insert a script that loads the API from the specified URL.
After key= insert your API key, which you can get here.
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
.google-map {
height: 300px;
background-color: #26263F;
// hide bottom labels
.gm-style-cc {
display: none;
}
// hide google label
a[href^="http://maps.google.com/maps"],
a[href^="https://maps.google.com/maps"],
a[href^="https://www.google.com/maps"] {
display: none !important;
}
}
// STANDARD GOOGLE MAP
function initMap() {
var mapBlock = $('.js-map');
if (!mapBlock.length) return;
var coordinateX = mapBlock.attr('data-coordinate-x'),
coordinateY = mapBlock.attr('data-coordinate-y'),
coordinates = new google.maps.LatLng(coordinateX, coordinateY);
var map = new google.maps.Map(mapBlock.get(0), {
center: coordinates,
zoom: 12,
disableDefaultUI: true,
zoomControl: true,
fullscreenControl: true,
zoomControlOptions: {
position: google.maps.ControlPosition.RIGHT_CENTER
},
// OPTIONAL - styles for theme (https://snazzymaps.com)
// minify code (https://www.minifier.org)
// styles: [{"featureType":"administrative","elementType":"all","stylers":[{"saturation":"-100"}]},{"featureType":"administrative.province","elementType":"all","stylers":[{"visibility":"off"}]},{"featureType":"landscape","elementType":"all","stylers":[{"saturation":-100},{"lightness":65},{"visibility":"on"}]},{"featureType":"poi","elementType":"all","stylers":[{"saturation":-100},{"lightness":"50"},{"visibility":"simplified"}]},{"featureType":"road","elementType":"all","stylers":[{"saturation":"-100"}]},{"featureType":"road.highway","elementType":"all","stylers":[{"visibility":"simplified"}]},{"featureType":"road.arterial","elementType":"all","stylers":[{"lightness":"30"}]},{"featureType":"road.local","elementType":"all","stylers":[{"lightness":"40"}]},{"featureType":"transit","elementType":"all","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"water","elementType":"geometry","stylers":[{"hue":"#ffff00"},{"lightness":-25},{"saturation":-97}]},{"featureType":"water","elementType":"labels","stylers":[{"lightness":-25},{"saturation":-100}]}]
});
// OPTIONAL - custom icon
var icon = {
url: '../img/svg/icon_map_marker.svg',
scaledSize: new google.maps.Size(50, 50)
};
var marker = new google.maps.Marker({
position: coordinates,
map: map,
// OPTIONAL - animation of marker
// animation: google.maps.Animation.BOUNCE,
// OPTIONAL - text on marker hover
// title: 'text on hover',
// OPTIONAL - custom icon from png or svg
// icon: icon
});
// OPTIONAL - add info window
var infoWindow = new google.maps.InfoWindow({
content: '<p>Info window</p>'
});
// show info window after click
/*marker.addListener('click', function() {
infoWindow.open(map, marker);
});*/
// show info window all time
/*google.maps.event.addListenerOnce(map, 'tilesloaded', function() {
infoWindow.open(map,marker);
});*/
// close info window after click anywhere on the map
google.maps.event.addListener(map, 'click', function() {
infoWindow.close();
});
}
initMap();
// GOOGLE MAP WITH MULTIPLE MARKER
function initMapMultipleMarkers() {
var mapBlock = $('.js-map-multiple-markers');
if (!mapBlock.length) return;
var map = new google.maps.Map(mapBlock.get(0), {
disableDefaultUI: true,
zoomControl: true,
fullscreenControl: true,
zoomControlOptions: {
position: google.maps.ControlPosition.RIGHT_CENTER
}
});
// array of markers (with coordinates, name, address)
var multipleMarkers = [
{
lat: 40.679680,
lng: -73.942175,
name: 'Name 1',
address: 'Address 1'
},
{
lat: 40.729391,
lng: -74.076758,
name: 'Name 2',
address: 'Address 2'
},
{
lat: 40.745260,
lng: -73.997794,
name: 'Name 3',
address: 'Address 3'
}
];
var infoWindow = new google.maps.InfoWindow();
google.maps.event.addListener(map, 'click', function() {
infoWindow.close();
});
// Determine the boundaries of the visible area of the map in accordance with the position of the markers
var bounds = new google.maps.LatLngBounds();
// create the markers
for (var i = 0; i < multipleMarkers.length; i++){
var latLng = new google.maps.LatLng(multipleMarkers[i].lat, multipleMarkers[i].lng);
var name = multipleMarkers[i].name;
var address = multipleMarkers[i].address;
addMarker(latLng, name, address);
// Expand the boundaries of our visible area by adding the coordinates of our current marker
bounds.extend(latLng);
}
// Automatically scale the map so that all markers are in the visible area of the map
map.fitBounds(bounds);
function addMarker(latLng, name, address) {
var marker = new google.maps.Marker({
position: latLng,
map: map,
title: name
});
google.maps.event.addListener(marker, 'click', function() {
var contentString = '<div class="infowindow">' +
'<h5>' + name + '</h5>' +
'<p>' + address + '</p>' +
'</div>';
infoWindow.setContent(contentString);
infoWindow.open(map, marker);
});
}
}
initMapMultipleMarkers();
// PULSE ANIMATION
$marker-color: #fff;
$marker-dot-width: 8px;
$marker-dot-height: $marker-dot-width;
$marker-end-size: 120px;
$animation-duration: 2.5s;
@mixin markerEndSize($circleEndSize){
width: $circleEndSize;
height: $circleEndSize;
}
.html-map-marker {
position: absolute;
z-index: 100;
width: $marker-dot-width;
height: $marker-dot-height;
background-color: $marker-color;
border-radius: 50%;
&:before,
&:after {
content: '';
background-color: rgba($marker-color, 1);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
transform-origin: center center;
animation: pulse $animation-duration linear infinite;
}
&:after {
animation-delay: $animation-duration/2;
}
}
@keyframes pulse {
0% {
@include markerEndSize(0);
background-color: rgba($marker-color, 0);
}
50% {
background-color: rgba($marker-color, 0.2);
}
70% {
background-color: rgba($marker-color, 0.4);
}
100% {
@include markerEndSize($marker-end-size);
background-color: rgba($marker-color, 0);
}
}
// MAP WITH HTML MARKER
function initMapWithHtmlMarker() {
var mapBlock = $('.js-map-with-marker');
if (!mapBlock.length) return;
var coordinateX = mapBlock.attr('data-coordinate-x'),
coordinateY = mapBlock.attr('data-coordinate-y'),
coordinates = new google.maps.LatLng(coordinateX, coordinateY);
var map = new google.maps.Map(mapBlock.get(0), {
center: coordinates,
zoom: 12,
disableDefaultUI: true,
zoomControl: false,
fullscreenControl: false,
styles: [{"featureType":"all","elementType":"labels.text.fill","stylers":[{"saturation":36},{"color":"#000000"},{"lightness":40}]},{"featureType":"all","elementType":"labels.text.stroke","stylers":[{"visibility":"on"},{"color":"#000000"},{"lightness":16}]},{"featureType":"all","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"administrative","elementType":"geometry.fill","stylers":[{"color":"#000000"},{"lightness":20}]},{"featureType":"administrative","elementType":"geometry.stroke","stylers":[{"color":"#000000"},{"lightness":17},{"weight":1.2}]},{"featureType":"landscape","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":20}]},{"featureType":"poi","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":21}]},{"featureType":"road.highway","elementType":"geometry.fill","stylers":[{"color":"#000000"},{"lightness":17}]},{"featureType":"road.highway","elementType":"geometry.stroke","stylers":[{"color":"#000000"},{"lightness":29},{"weight":0.2}]},{"featureType":"road.arterial","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":18}]},{"featureType":"road.local","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":16}]},{"featureType":"transit","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":19}]},{"featureType":"water","elementType":"geometry","stylers":[{"color":"#000000"},{"lightness":17}]}]
});
// HTML MAP MARKER
function CustomMarker(latlng, map, args) {
this.latlng = latlng;
this.args = args;
this.setMap(map);
}
CustomMarker.prototype = new google.maps.OverlayView();
CustomMarker.prototype.draw = function() {
var self = this;
var div = this.div;
if (!div) {
div = this.div = document.createElement('div');
div.className = 'html-map-marker';
if (typeof(self.args.marker_id) !== 'undefined') {
div.dataset.marker_id = self.args.marker_id;
}
google.maps.event.addDomListener(div, "click", function(event) {
// marker click
google.maps.event.trigger(self, "click");
});
var panes = this.getPanes();
panes.overlayImage.appendChild(div);
}
var point = this.getProjection().fromLatLngToDivPixel(this.latlng);
var mapMarkerWidth = $('.html-map-marker').width() / 2;
if (point) {
div.style.left = (point.x - mapMarkerWidth) + 'px';
div.style.top = (point.y - mapMarkerWidth) + 'px';
}
};
CustomMarker.prototype.remove = function() {
if (this.div) {
this.div.parentNode.removeChild(this.div);
this.div = null;
}
};
CustomMarker.prototype.getPosition = function() {
return this.latlng;
};
var overlay = new CustomMarker(
coordinates,
map,
{
marker_id: 'html-map-murker'
}
);
// HTML MAP MARKER END
}
initMapWithHtmlMarker();
Embed a YouTube or Vimeo video on web-site and make it responsive. You can set aspect ratios by modifier classes: class="-ratio-16-9" or class="-ratio-4-3" or class="-ratio-1-1".
Do not forget to insert your VIDEO_ID to iframe.
.responsive-video {
position: relative;
&__item {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
width: 100%;
height: 100%;
border: 0;
}
&.-ratio-16-9 {
padding-bottom: 56.25%;
}
&.-ratio-4-3 {
padding-bottom: 75%;
}
&.-ratio-1-1 {
padding-bottom: 100%;
}
}
Making YouTube thumbnails based on iframe. At the beginning - we show thumbnail, and hide iframe. After click - hide thumbnail and show iframe (with autoplay attribute). By this method the page is loaded without iframes which speeds up the loading of the site.
Do not forget to insert your VIDEO_ID to iframe.
.youtube-thumbnail {
position: relative;
cursor: pointer;
padding-bottom: 56.25%;
background-color: #000000;
&__media {
position: absolute;
right: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
&.-bg-image {
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
&:hover {
.youtube-icon__figure {
transition: fill .1s cubic-bezier(0.0,0.0,0.2,1),fill-opacity .1s cubic-bezier(0.0,0.0,0.2,1);
fill: #f00;
fill-opacity: 1;
}
}
}
&__icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.youtube-icon {
width: 68px;
height: 48px;
&__figure {
transition: fill .1s cubic-bezier(0.4,0.0,1,1),fill-opacity .1s cubic-bezier(0.4,0.0,1,1);
}
}
// YOUTUBE THUMBNAIL
function initYouTubeThumbnail() {
function getYouTubeVideoId(videoItem) {
var videoThumbnailUrl = videoItem.find('.js-youtube-thumbnail-media').attr('style');
return videoThumbnailUrl.split('/')[videoThumbnailUrl.split('/').length - 2];
}
function insertYouTubeIframe() {
$('.js-youtube-thumbnail').on('click', function() {
var youTubeIframe = $('<iframe class="youtube-thumbnail__media" allowfullscreen></iframe>');
youTubeIframe.attr('allow', 'autoplay');
youTubeIframe.attr('src', 'https://www.youtube.com/embed/' + getYouTubeVideoId($(this)) + '?rel=0&showinfo=0&autoplay=1');
$(this).find('.js-youtube-thumbnail-media').remove();
$(this).append(youTubeIframe);
})
}
insertYouTubeIframe();
}
initYouTubeThumbnail();
Making Vimeo thumbnails based on iframe. At the beginning - we show thumbnail, and hide iframe. After click - hide thumbnail and show iframe (with autoplay attribute). By this method the page is loaded without iframes which speeds up the loading of the site.
Do not forget to insert your VIDEO_ID to iframe.
.vimeo-thumbnail {
position: relative;
cursor: pointer;
padding-bottom: 56.25%;
background-color: #000000;
&__media {
position: absolute;
right: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
&.-bg-image {
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
&:hover {
.vimeo-icon {
background-color: #3CAEF2;
}
}
}
&__icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.vimeo-icon {
width: 65px;
height: 40px;
transition: opacity 250ms ease-out,background-color 40ms,color 40ms;
background: rgba(23,35,34,.75);
border-radius: .5em;
&__icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 20px;
height: 20px;
}
}
// VIMEO THUMBNAIL
function initVimeoThumbnail() {
function getVimeoVideoId(vimeoBlock) {
return vimeoBlock.attr('data-vimeo-url').split('/').pop();
}
function setVimeoThumbnailBackground(vimeoBlock) {
$.getJSON('https://vimeo.com/api/v2/video/' + getVimeoVideoId(vimeoBlock) + '.json', function (data) {
var thumbnailImg = data[0].thumbnail_large;
vimeoBlock.find('.js-vimeo-thumbnail-media').css('background-image', 'url(' + thumbnailImg + ')');
});
}
function setVimeoThumbnailBackgroundToAllBlocks() {
$('.js-vimeo-thumbnail').each(function() {
setVimeoThumbnailBackground($(this));
});
}
setVimeoThumbnailBackgroundToAllBlocks();
function insertVimeoIframe() {
$('.js-vimeo-thumbnail').on('click', function() {
var vimeoIframe = $('<iframe class="vimeo-thumbnail__media" allowfullscreen></iframe>');
vimeoIframe.attr('allow', 'autoplay');
vimeoIframe.attr('src', 'https://player.vimeo.com/video/' + getVimeoVideoId($(this)) + '?autoplay=1');
$(this).find('.js-vimeo-thumbnail-media').remove();
$(this).append(vimeoIframe);
})
}
insertVimeoIframe();
}
initVimeoThumbnail();
Background video for the block. May be useful for background video for a section.
This component is dependent on jquery-background-video.
.bg-video-block {
position: relative;
height: 300px;
&__html-bg-video {
position: absolute;
min-width: 100%;
min-height: 100%;
width: auto;
height: auto;
top: 50%;
left: 50%;
object-fit: cover;
transform: translate(-50%,-50%);
opacity: 0;
transition: opacity 300ms linear;
&.is-visible {
opacity: 1;
}
}
}
function initBackgroundVideo() {
if ($('.js-html-bg-video').length) {
$('.js-html-bg-video').bgVideo({
showPausePlay: false,
pauseAfter: 0
});
}
}
initBackgroundVideo();