Stories в Тильде

Скрипт добавляет блок в стиле Stories из запрещенограма
<div class="stories-container">
    <div class="stories-wrapper">
        <div class="stories-progress">
            <!-- Прогресс-бары генерируются JavaScript -->
        </div>
        
        <div class="stories-content">
            <!-- Stories контент -->
        </div>
        
        <button class="stories-nav stories-prev">
            <svg width="24" height="24" viewBox="0 0 24 24">
                <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
            </svg>
        </button>
        
        <button class="stories-nav stories-next">
            <svg width="24" height="24" viewBox="0 0 24 24">
                <path d="M8.59 7.41L10 6l6 6-6 6-1.41-1.41L13.17 12z"/>
            </svg>
        </button>
    </div>
</div>

<style>
.stories-container {
    position: relative;
    width: 100%;
    height: 100vh;
    overflow: hidden;
}

.stories-wrapper {
    position: relative;
    width: 100%;
    height: 100%;
}

.stories-progress {
    position: absolute;
    top: 20px;
    left: 0;
    right: 0;
    display: flex;
    gap: 4px;
    padding: 0 16px;
    z-index: 10;
}

.progress-bar {
    flex: 1;
    height: 2px;
    background: rgba(255, 255, 255, 0.3);
    border-radius: 2px;
}

.progress-bar-fill {
    width: 0;
    height: 100%;
    background: #fff;
    border-radius: 2px;
    transition: width 0.1s linear;
}

.stories-content {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    transition: transform 0.3s ease;
}

.story-item {
    min-width: 100%;
    height: 100%;
    background-size: cover;
    background-position: center;
}

.stories-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: rgba(255, 255, 255, 0.1);
    border: none;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.3s;
    z-index: 10;
}

.stories-nav svg {
    fill: #fff;
}

.stories-prev {
    left: 16px;
}

.stories-next {
    right: 16px;
}

.stories-nav:hover {
    background: rgba(255, 255, 255, 0.2);
}

@media (max-width: 768px) {
    .stories-nav {
        display: none;
    }
}
</style>

<script>
class Stories {
    constructor(container) {
        this.container = container;
        this.wrapper = container.querySelector('.stories-wrapper');
        this.content = container.querySelector('.stories-content');
        this.progressContainer = container.querySelector('.stories-progress');
        
        this.stories = [
            { background: 'https://static.tildacdn.com/5335cb84-d1f6-4fe4-ad8d-89c9565dc051/photo14533964506733fe83d2db2c4.jpeg', duration: 5000 },
            { background: 'https://static.tildacdn.com/d035c716-f5b0-424c-8b24-443d44ac1832/photo14539043002350f2f60b15b5d1.jpeg', duration: 5000 },
            { background: 'https://static.tildacdn.com/4c650eff-0409-417d-a8d0-5922c110ded3/photo14539292030625b1b9cb4cb941.jpeg', duration: 5000 },
            // Добавьте свои истории здесь
        ];
        
        this.currentStory = 0;
        this.init();
    }
    
    init() {
        this.createStories();
        this.createProgressBars();
        this.bindEvents();
        this.play();
    }
    
    createStories() {
        this.stories.forEach(story => {
            const div = document.createElement('div');
            div.className = 'story-item';
            div.style.backgroundImage = `url(${story.background})`;
            this.content.appendChild(div);
        });
    }
    
    createProgressBars() {
        this.stories.forEach(() => {
            const bar = document.createElement('div');
            bar.className = 'progress-bar';
            const fill = document.createElement('div');
            fill.className = 'progress-bar-fill';
            bar.appendChild(fill);
            this.progressContainer.appendChild(bar);
        });
    }
    
    bindEvents() {
        this.container.querySelector('.stories-prev').addEventListener('click', () => this.prev());
        this.container.querySelector('.stories-next').addEventListener('click', () => this.next());
        
        this.container.addEventListener('mouseenter', () => this.pause());
        this.container.addEventListener('mouseleave', () => {
            this.play();
        });
        
        let touchStartX = 0;
        this.container.addEventListener('touchstart', e => {
            touchStartX = e.touches[0].clientX;
            this.pause();
        });
        
        this.container.addEventListener('touchend', e => {
            const touchEndX = e.changedTouches[0].clientX;
            const diff = touchStartX - touchEndX;
            
            if (Math.abs(diff) > 50) {
                if (diff > 0) this.next();
                else this.prev();
            }
            this.play();
        });
    }
    
    play() {
        const currentStoryData = this.stories[this.currentStory];
        const progressBar = this.progressContainer.children[this.currentStory].querySelector('.progress-bar-fill');
        
        let startTime = null;
        const animate = timestamp => {
            if (!startTime) startTime = timestamp;
            const progress = timestamp - startTime;
            
            const percentage = Math.min(progress / currentStoryData.duration * 100, 100);
            progressBar.style.width = `${percentage}%`;
            
            if (progress < currentStoryData.duration) {
                this.animationFrame = requestAnimationFrame(animate);
            } else {
                this.next();
            }
        };
        
        this.animationFrame = requestAnimationFrame(animate);
    }
    
    pause() {
        cancelAnimationFrame(this.animationFrame);
    }
    
    next() {
        this.currentStory++;
        if (this.currentStory >= this.stories.length) {
            this.currentStory = 0;
        }
        this.updateStory();
    }
    
    prev() {
        this.currentStory--;
        if (this.currentStory < 0) {
            this.currentStory = this.stories.length - 1;
        }
        this.updateStory();
    }
    
    updateStory() {
        this.content.style.transform = `translateX(-${this.currentStory * 100}%)`;
        
        this.progressContainer.querySelectorAll('.progress-bar-fill').forEach((fill, index) => {
            fill.style.width = index < this.currentStory ? '100%' : '0%';
        });
        
        cancelAnimationFrame(this.animationFrame);
        this.play();
    }
}

// Инициализация
document.addEventListener('DOMContentLoaded', () => {
    const storiesContainer = document.querySelector('.stories-container');
    new Stories(storiesContainer);
});
</script>