$(function(){ var $carousel; //the cardSet function is called when sensor sets a card on load (through data-on-set in html) window.cardSet=function(checked,arg){ //only if a value is checked, because interactions with no selection can also be saved and shouldn fold in if (checked.checked){ //check if answered with as parameter all the cards in this interaction (to check which card should be on top) //the interaction got a data-trick element with the same name as the interaction.name on the radio's //dbd : dirty but delicious checkIfAnswered($('.card[data-trick="trick-'+arg.name+'"]')); } }; function checkIfAnswered(cards){ //get the answer of a card in the cards (let's say: the first card) and add class answered to the parent //so the correct animation is triggered when clicked again let answer = cards.first().parent(); answer.addClass('answered'); //for every card, check look for a checked radio button inside the card. //if found, animation should trigger with this card and loop should be ended. cards.each(function(){ let card = $(this); let radios = $(this).find('input[type="radio"]'); radios.each(function(){ //if a radio is checked, fire animation but without checking and unchecking radio buttons in the animation, for this is already done by sensor if($(this).is(':checked')) { animateFoldIn(card,false); return; } }); }); } //When a radio button is clicked, the no bubbling effect on parent clicks should occur //Because the radio is inside the card, which also has a click event $('input[type="radio"]').on('click',function(e){ e.stopPropagation(); }); //a radiobutton needs to be wrapped in a label to behave, so the click event is on the label instead of the card element $('.card label').on('click', function(e) { //prevent everything anyone thinks of doing on click e.preventDefault(); //define the jQuery elements for easier access let card = $(this).parent(); let answer = card.parent(); //toggle if an answered is given or not given, so the correct animation can trigger answer.toggleClass('answered'); if(answer.hasClass('answered')){ //animate folding in with the selected card on top animateFoldIn(card); //and check correct answer } else { //animate folding out all cards of this question (which is why card is a parameter) animateFoldOut(card); //and uncheck all radio's } }); function animateFoldIn(clickedCard, check=true){ //container (answer) div let answer = clickedCard.parent(); // the wrappers clientRect, with width, height, left, top, bottom in px let wrapper = answer[0].getBoundingClientRect(); //for easier accessibility in gsap let wrapperWidth = wrapper.width; let wrapperLeft = wrapper.x; //magic number :), margin is set to 10px in css, for later iteration: get margin from element.styles('margin') or smthn let margin = 10; //index, to check if the card is the selected card, and for calculating different z-indexes for the not chosen cards let index = clickedCard.data('index'); //find all the cards, make a gsaparray let targets = gsap.utils.toArray(answer.find('.card')); let rotationSwitcher = 1; //animate each card targets.forEach(target => { rotationSwitcher = rotationSwitcher*-1; //get a random integer for rotating the other cards let randomRotation =randomInteger(10,25)*rotationSwitcher; //first animate zIndex without delay and duration, so it happends instantly, otherwise animation looks shocky gsap.to(target, { duration:0, delay:0, //is the index the index of the chosen card, z-index = 10, else z-index is 10-cardIndex, +1 because index starts at 0 zIndex: (targets.indexOf(target) === index) ? 10 : Math.max(1, 10 - (index+1)), }); //then animate position, rotation and opacity gsap.to(target, { duration: 0.5, //wrapper.top is the same as wrapper.y, so wrapper.y - own card.y, +margin of 10, so that the y target becomes the top of the wrapper y: wrapper.y + margin - target.getBoundingClientRect().y, //it's the middle of the wrapper basically. at some point I understood this and it worked. math. x: wrapper.x+(wrapper.width/2)- ((target.getBoundingClientRect().left)+(target.getBoundingClientRect().width/2)), //only rotate if index != currentCard(target) rotation: (targets.indexOf(target) === index) ? 0 : randomRotation, //only decrease opacity if index != currentCard(target) opacity: (targets.indexOf(target) === index) ? 1 : 0.75, //delay for a bit of staggering effect delay: 0.2, }); }); //check the radio of the clicked card //only check if check = true, which is default (but not if sensor set it on init ;) ) if(check) { checkRadio(clickedCard)}; } function animateFoldOut(clickedCard){ //container div (answer) let answer = clickedCard.parent(); //all cards in the answer let target = answer.find('.card'); gsap.to(target, { duration: 0.5, //transform x,y (0,0) goes back to the position the cards had through CSS, which is exactly what we want x: 0, y:0, opacity:1, rotation:0, stagger: { amount: 0.2 }, }); //zIndex AFTER other animation, so it doesnt look shocky again gsap.to(target, { duration: 0, zIndex:1, //3 times stagger amount = 0.6 delay:0.6, }); //uncheck all radio's uncheckRadio(clickedCard); } function checkRadio(card){ //the targetRadio is the radio inside the clicked card let targetRadio = card.find('input[type=radio]'); //check the target and trigger click so it transmits //targetRadio.attr('checked', true); targetRadio.trigger("click"); setTimeout(function(){ let slide = $('#cardcarousel').flickity().data('flickity').selectedIndex; let lastSlide = $('#cardcarousel').flickity().data('flickity').slides.length -1; if (slide == lastSlide) { modalClose(); } else { $('#cardcarousel').flickity('next'); } }, 700); // targetRadio.trigger('update'); } function uncheckRadio(card){ //in this card, find the radios of sibling cards (through parent) let parent = card.parent(); let radios = parent.find('input[type=radio]'); //find the clickedRadio and trigger click, just for transmitting purposes let targetRadio = card.find('input[type=radio]'); //targetRadio.trigger("click"); //uncheck 'm all radios.each(function() { if ($(this).is(':checked')) { $(this).trigger("deselect"); $(this).prop('checked', false); } }); } //create random int within a reach of min and max function randomInteger(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } 'use strict'; var modals={}; var currentmodal; // DOM ready, take it away gsap.config({ nullTargetWarn: false, }); $('.open-modal').each(function(){ activateModal('modal-'+$(this).attr('data-uid'), $(this)); }); function activateModal(id, $open) { var $modal=$('#'+id); if ($modal.length) { // sliders $carousel = $modal.find('.slideshow'); $carousel.flickity({ cellAlign: 'center', contain: false, freeScroll: false, prevNextButtons: $modal.find(".slide").length>1, pageDots: $modal.find(".slide").length>1, wrapAround: true, adaptiveHeight: false, on: { change: function( index ) { // make sure any playing video is paused $('.video-player').each(function(player){ $(this).get(0).pause(); }) ; } } }); // center modal if content has less height then 500px if ($modal.find(".flickity-viewport").length) { var flickityVieport = $modal.find('.flickity-viewport'); var flickityVieportHeight = flickityVieport.height(); if (flickityVieportHeight < 500) { $.each(modals, function (i, modal) { $modal.css('align-items', 'center'); }); } } var $content=$modal.find('.modal-content'); // open modal on click $open.on('click', function () { modalOpen(id); let slideNumber = $open.data('page-index'); if (slideNumber !== null && slideNumber !== '') { //$carousel.flickity('select', slideNumber-1); $carousel.flickity('selectCell', '.slide-'+slideNumber); } }); const openModal = gsap.timeline({paused: true}); /* //fade animation self openModal.from($content, { delay: 0, duration: .4, ease: Power4.easeInOut, autoAlpha:1, y:'244px' }); //fadce animation background openModal.to($modal, { duration: .4, ease: Power4.easeInOut, autoAlpha:1 }); */ openModal.to($modal, { duration: .6, ease: Power4.easeOut, autoAlpha:1, onStart: function() { $('html').addClass('no-scroll'); }, }); openModal.from($content, { delay: -.3, duration: .6, ease: Power4.easeOut, autoAlpha:0, y:'0px', }); modals[id]=openModal; } } // open modal function modalOpen(modalId) { currentmodal= modals[modalId]; currentmodal.play().timeScale(1); } // close modal function modalClose(modalId) { if (currentmodal!==undefined) currentmodal.reverse().timeScale(-2) $('video').each(function() { $(this).get(0).pause(); }); $('html').removeClass('no-scroll'); } // close modal $('.modal-close').on('click', function () { modalClose(); }); $('.modal').click(function (event) { if (!$(event.target).closest('.modal-content').length && !$(event.target).is('.modal-content')) { modalClose(); } }); }); /*******************************************************************/ /* ftrtch dino */ /* [type: JS] [file:main-i-cards] [143.3888] DESIGN*/ /*******************************************************************/ /* 0.2 D>D */ /* db 194 */