От автора: не так давно я написал небольшой скрипт, автоматизирующий процесс создания адаптивной карусели изображений. Такую галерею можно создать и на чистом CSS, однако в таком случае при смене количества изображений пересчитывалась бы keyframe анимация. Мой метод на JavaScript сохраняет все преимущества производительности способа на чистом CSS при помощи широкого ряда опций при написании keyframe анимации.
Представленный ниже скрипт выполняет точно такую же задачу, он плавно показывает изображения: еще раз, вы можете добавлять сколь угодно изображений в любом порядке. Единственное требование – изображения должны быть одного размера:
1 2 3 4 5 6 |
<figure id="fadey"> <img src="dancer-arch.jpg" alt> <img src="white-bridge-jump.jpg" alt> <img src="dancer-beams.jpg" alt> <img src="bridge-white-walker.jpg" alt> </figure> |
Для каждого изображения должен быть заполнен атрибут alt; мы оставили его пустым для экономии места. Для достижения лучшего эффекта советую использовать атрибут srcset, w дескриптор и атрибут sizes:
1 2 3 |
<img src="dancer-arch.jpg" srcset="dancer-arch.jpg 750w, dancer-arch-2x.jpg 1500w" sizes="(min-width: 750px) 750px, 100vw"> |
Также необходимо добавить пару базовых стилей для форматирования изображений и контейнера галереи:
1 2 3 4 5 6 7 8 |
#fadey { width: 100%; position: relative; } #fadey img { display: block; width: 100%; } |
#fadey должен иметь 100% ширину от своего контейнера. Если #fadey это тег figure, то левый margin должен быть нулевым:
1 |
margin: 0 auto |
Скрипт добавляется в конец страницы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
var cssFadey = function(newOptions) { var options = (function() { var mergedOptions = {}, defaultOptions = { presentationTime: 3, durationTime: 2, fadeySelector: '#fadey', cssAnimationName: 'fadey', fallbackFunction: function() {} }; for (var option in defaultOptions) mergedOptions[option] = defaultOptions[option]; for (var option in newOptions) mergedOptions[option] = newOptions[option]; return mergedOptions; })(), CS = this; CS.animationString = 'animation'; CS.hasAnimation = false; CS.keyframeprefix = ''; CS.domPrefixes = 'Webkit Moz O Khtml'.split(' '); CS.pfx = ''; CS.element = document.getElementById(options.fadeySelector.replace('#', '')); CS.init = (function() { if (CS.element.style.animationName !== undefined) CS.hasAnimation = true; if (CS.hasAnimation === false) { for (var i = 0; i < CS.domPrefixes.length; i++) { if (CS.element.style[CS.domPrefixes[i] + 'AnimationName'] !== undefined) { CS.pfx = domPrefixes[i]; CS.animationString = pfx + 'Animation'; CS.keyframeprefix = '-' + pfx.toLowerCase() + '-'; CS.hasAnimation = true; break; } } } if (CS.hasAnimation === true) { function loaded() { var imgAspectRatio = firstImage.naturalHeight / (firstImage.naturalWidth / 100); var imageCount = CS.element.getElementsByTagName("img").length, totalTime = (options.presentationTime + options.durationTime) * imageCount, css = document.createElement("style"); css.type = "text/css"; css.id = options.fadeySelector.replace('#', '') + "-css"; css.innerHTML += "@" + keyframeprefix + "keyframes " + options.cssAnimationName + " {\n"; css.innerHTML += "0% { opacity: 1; }\n"; css.innerHTML += (options.presentationTime / totalTime) * 100+"% { opacity: 1; }\n"; css.innerHTML += (1/imageCount)*100+"% { opacity: 0; }\n"; css.innerHTML += (100-(options.durationTime/totalTime*100))+"% { opacity: 0; }\n"; css.innerHTML += "100% { opacity: 1; }\n"; css.innerHTML += "}\n"; css.innerHTML += options.fadeySelector + " img { position: absolute; top: 0; left: 0; " + keyframeprefix + "animation: " + options.cssAnimationName + " " + totalTime + "s ease-in-out infinite; }\n"; css.innerHTML += options.fadeySelector + "{ box-sizing: border-box; padding-bottom: " + imgAspectRatio + "%; }\n"; for (var i=0; i < imageCount; i++) { css.innerHTML += options.fadeySelector + " img:nth-last-child("+(i+1)+") { " + keyframeprefix + "animation-delay: "+ i * (options.durationTime + options.presentationTime) + "s; }\n"; } document.body.appendChild(css); } var firstImage = CS.element.getElementsByTagName("img")[0]; if (firstImage.complete) { loaded(); } else { firstImage.addEventListener('load', loaded); firstImage.addEventListener('error', function() { alert('error'); }) } } else { // fallback function options.fallbackFunction(); } })(); } |
Вызывается скрипт через:
1 |
cssFadey(); |
Существует несколько возможных опций вызова функции, про которые я расскажу чуть ниже. Быстро пробежимся по стандартным настройкам:
presentationTime – время отображения каждого изображения на экране, измеряется в секундах (по умолчанию 3 секунды)
transitionTime – время перехода от изображения и изображению, также измеряется в секундах (по умолчанию 2 секунды)
fadeySelector — id контейнера изображений (по умолчанию это
cssAnimationName – название генерируемой скриптом keyframe анимации
fallbackFunction – JavaScript функция, которую вы хотите запустить, если браузер не поддерживает CSS анимацию
Скрипт проверяет, нужны ли браузеру вендорные префиксы для CSS анимации (в современных браузерах это уже редкость)
Скрипт создает анимацию для каждого изображения и помещает эти стили в самый низ страницы. Для анимации из 4 изображений с задержкой в 4 секунды для каждого и переходом в 2 секунды будет сгенерировано следующее:
1 2 3 4 5 6 7 |
@keyframes fadey { 0% { opacity: 1; } 16.66% { opacity: 1; } 25% { opacity: 0; } 91.66% { opacity: 0; } 100% { opacity: 1; } } |
Все изображения в контейнере запускают одну и ту же анимацию:
1 2 3 4 |
#fadey img { position: absolute; top: 0; animation: fadey 24s ease-in-out infinite; } |
Так как абсолютное позиционирование изображений не будет влиять на высоту контейнера, то padding-top для него устанавливается в процентах. Данное значение в процентах высчитывается путем нахождения первого изображения в контейнере и деления его высоты на ширину:
1 |
imgAspectRatio = firstImage.naturalHeight / (firstImage.naturalWidth / 100) |
Полное время анимации равно [количество изображений]*([ presentationTime]+[ transitionTime])
Каждому изображению задается значение animation-delay. При помощи nth-last-child сгенерированный CSS ведет отсчет с последнего изображения в контейнере (которое, из-за абсолютного позиционирования находится в самом верху стека изображений). Для примера выше будет сгенерирован следующий CSS код:
1 2 3 4 |
#fadey img:nth-last-child(1) { animation-delay: 0s; } #fadey img:nth-last-child(2) { animation-delay: 6s; } #fadey img:nth-last-child(3) { animation-delay: 12s; } #fadey img:nth-last-child(4) { animation-delay: 18s; } |
Если вы хотите вызвать скрипт со своими опциями по разметке:
1 2 3 |
<figure id="gallery"> … </figure> |
Напишите следующий код:
1 2 3 4 5 6 |
cssFadey({ presentationTime: 3, durationTime: 2, fadeySelector: '#gallery', cssAnimationName: 'sequence' }); |
Я создал демо на CodePen, которое вы можете дополнить. Единственный недостаток данного способа в том, что нет способа перепрыгнуть вручную с одного изображения на другое. Для добавления ручного управления необходимо будет воспользоваться Web Animation API, чтобы не понизить эффективность CSS анимации. Об этом я расскажу уже в следующей статье.
Источник: //thenewcode.com/
Редакция: Команда webformyself.