Home > Software engineering >  Transition between colspan changes in Tailwind
Transition between colspan changes in Tailwind

Time:11-26

I have some javascript which will change the col-span-{x} class of a div to correctly give the element its width on the page. I would like this change to be animated over a second instead of an instant jump.

<div >...</div>

CodePudding user response:

As per the Tailwind docs, col-span-{number} classes are setting the grid-column CSS property (MDN), namely class names col-span-1 and col-span-2 correspond to grid-column: span 1 / span 1; and grid-column: span 2 / span 2;.

These grid-column shorthand property and its values are not animatable. So, you have to fight the headwind of animating the widths yourself, in a tedious way like this:

  1. clone the animated grid cell element and its next neighbour
  2. add transition: width 1s; to the style of clones
  3. put the clones exactly over their originals using element.getBoundingClientRect() for their pixel position and dimensions
  4. change the col-span-{number} class names in the origin elements as needed
  5. get the pixel position and dimensions of the originals with new col-span-{number} classes
  6. feed new widths to the clone elements
  7. on the transitionend event, remove the clones and show the originals
  8. Deal with the literal edge-cases when you change the col-span-{number} on an edgemost item.

Well, here's a demo that needs refactoring and resembles the good old Masonry layout.

let grid; 

const hide = (elem) => {
    elem.style.opacity = 0;
}

const show = (elem) => {
    elem.style.opacity = 1;
}

const animateWidth = (elem) => {
    const gridRect = grid.getBoundingClientRect();
    const targetRect = elem.getBoundingClientRect();
    
    const clone = elem.cloneNode();
    clone.style.pointerEvents = 'none';
    clone.innerHTML = elem.innerHTML   ' clone';
    clone.classList.add('clone');
    clone.style.position = 'absolute';
    clone.style.zIndex = 999;
    clone.style.transition = 'width 1s, left 1s, top 1s';
    
    const oldWidth = targetRect.width;
    clone.style.width = oldWidth   'px';
    
    const top = targetRect.y - gridRect.y;
    clone.style.top = top ? top   'px' : 0;
    
    const left = targetRect.x - gridRect.x;
    clone.style.left = left ? left   'px' : 0;

    grid.appendChild(clone);

    hide(elem);
    
    let nextSibling;
    let nextSiblingClone;
    if (elem.nextSibling && !elem.nextSibling.classList.contains('clone')) {
        nextSibling = elem.nextSibling;
        nextSiblingClone = createAndGetNextSiblingClone(elem.nextSibling);
    }
    
    elem.classList.toggle('col-span-1');
    elem.classList.toggle('col-span-2');
    
    const newWidth = elem.getBoundingClientRect().width;
    clone.style.width = newWidth ? newWidth   'px' : 0;
    
    const newLeft = elem.getBoundingClientRect().x - gridRect.x;
    clone.style.left = newLeft ? newLeft   'px' : 0;
    
    const newTop = elem.getBoundingClientRect().y - gridRect.y;
    clone.style.top = newTop ? newTop   'px' : 0;
    
    if (nextSibling) {
        const nextSiblingRect = nextSibling.getBoundingClientRect();
        
        const nextSiblingLeft = nextSiblingRect.x - gridRect.x;
        nextSiblingClone.style.left = nextSiblingLeft ? nextSiblingLeft   'px' : 0;
        
        const nextSiblingTop = nextSiblingRect.y - gridRect.y;
        nextSiblingClone.style.top = nextSiblingTop ? nextSiblingTop   'px' : 0;
    }
    
    clone.ontransitionend = (event) => {
        show(elem);
    if (event && event.target && event.target.parentNode) {
        event.target.parentNode.removeChild(event.target);
    }
    }
}

const createAndGetNextSiblingClone = (elem) => {
    const gridRect = grid.getBoundingClientRect();
    const targetRect = elem.getBoundingClientRect();
    
    const clone = elem.cloneNode();
    clone.innerHTML = elem.innerHTML   ' clone';
    clone.classList.add('clone');
    clone.style.position = 'absolute';
    clone.style.zIndex = 999;
    clone.style.transition = 'left 1s, top 1s';
    clone.style.pointerEvents = 'none';
    clone.style.width = targetRect.width ? targetRect.width   'px' : 0;
    
    const top = targetRect.y - gridRect.y;
    clone.style.top = top ? top   'px' : 0;
    
    const left = targetRect.x - gridRect.x;
    clone.style.left = left ? left   'px' : 0;
    
    grid.appendChild(clone);
    
    hide(elem);
    
    clone.ontransitionend = (event) => {
        show(elem);
    if (event && event.target && event.target.parentNode) {
        event.target.parentNode.removeChild(event.target);
    }
    }
    
    return clone;
}

const onGridItemClick = (event) => {
    animateWidth(event.target);
}

const displayTheGrid = () => {
    grid = document.getElementById('grid');
    
    'lightgreen lightblue #dabaf9 beige #ffbbbb #f9daba #99cefa'
        .split(' ')
        .forEach((color, index) => {
            const gridItem = document.createElement('div');
            gridItem.innerHTML = index;
            gridItem.style.backgroundColor = color;
            grid.appendChild(gridItem);
            gridItem.onclick = onGridItemClick;
        });
}

window.addEventListener('DOMContentLoaded', displayTheGrid);
* {
  box-sizing: border-box;
}

#grid {
  display: grid;
  grid-template-columns: 20fr 20fr 20fr 20fr 20fr;
  grid-gap: 10px;
  margin-top: 10px;
  position: relative;
}

#grid > div {
  border-radius: 6px;
  padding: 10px;
  background-color: silver;
}

.col-span-1 {
  grid-column: span 1 / span 1;
}

.col-span-2 {
  grid-column: span 2 / span 2;
}
<!-- https://stackoverflow.com/questions/74571960 -->
<div id="grid"></div>

CodePudding user response:

I don't know if with only transition property applied you can perform an animation on col-span.

Try to use transition-all instead of transition

  • Related