feat(web): showcase video

fix: showcase cards clipping and gradients
fix: touch scroll position
This commit is contained in:
ThaUnknown 2023-12-27 16:37:10 +01:00
parent 8b77da7400
commit af3f311232
7 changed files with 140 additions and 29 deletions

View file

@ -12,7 +12,7 @@
<SmallCard {media} preview={i === 1} />
{/each}
</div>
<div class='d-inline-block gallery' style:--top={(-1100 + ($scrollPosition - 800)) / 5 + 'px'}>
<div class='d-inline-block gallery' style:--top={(-1600 + ($scrollPosition - 800)) / 5 + 'px'}>
{#each cards.slice(4) as media}
<SmallCard {media} />
{/each}
@ -20,11 +20,6 @@
</div>
<style>
@media (pointer: none), (pointer: coarse){
.gallery {
margin-top: 0 !important;
}
}
.gallery {
margin-top: var(--top);
}

View file

@ -1,5 +1,8 @@
<script>
export let stargazers = []
/**
* @type {Promise<any>}
*/
export let stargazers
function * chunks (arr, n) {
for (let i = 0; i < arr.length; i += n) {
@ -32,7 +35,7 @@
<style>
.overlay-gradient {
background: linear-gradient(90deg, rgba(15,17,19,1) 0%, rgba(15,17,19,0) 25%, rgba(15,17,19,0) 75%, rgba(15,17,19,1) 100%);
background: linear-gradient(90deg, rgba(16,17,19,1) 0%, rgba(16,17,19,0) 25%, rgba(16,17,19,0) 75%, rgba(16,17,19,1) 100%);
pointer-events: none;
}
.stargazers {

View file

@ -0,0 +1,42 @@
<script>
import { getContext } from 'svelte'
import { scale } from 'svelte/transition'
import { quartOut } from 'svelte/easing'
const show = getContext('video-modal')
/**
* @type {HTMLDivElement}
*/
let modal
function close () {
$show = false
}
function checkClose ({ keyCode }) {
if (keyCode === 27) close()
}
$: $show && modal?.focus()
</script>
{#if $show}
<div class='modal z-40' class:show={$show}>
<div class='modal-dialog' on:pointerup|self={close} on:keydown={checkClose} tabindex='-1' role='button' bind:this={modal} transition:scale={{ duration: 500, opacity: 0.5, start: 0.9, easing: quartOut }}>
<div class='modal-content w-three-quarter h-full bg-transparent d-flex justify-content-center flex-column'>
<button class='close pointer z-30 top-20 right-0 position-absolute' type='button' on:click={close}> &times; </button>
<!-- eslint-disable-next-line svelte/valid-compile -->
<video src='/showcase.mp4' controls class='border rounded overflow-hidden' />
</div>
</div>
</div>
{/if}
<style>
.close {
top: 4rem !important;
left: unset !important;
right: 4rem !important;
}
.modal {
top: 0 !important
}
</style>

View file

@ -0,0 +1,60 @@
<script>
import { getContext } from 'svelte'
const show = getContext('video-modal')
/** @type {HTMLDivElement} */
let container
let top = 0
let left = 0
/** @param {MouseEvent} param0 */
function followMouse ({ clientX, clientY }) {
if (!clientX || !clientY) return
const containerRect = container.getBoundingClientRect()
left = clientX - containerRect.left
top = clientY - containerRect.top
}
</script>
<div class='position-relative play-container' on:mousemove={followMouse} bind:this={container} role='none'>
<button class='btn rounded-circle btn-square btn-lg d-flex align-items-center justify-content-center position-absolute z-100 w-50 h-50' style:--left={left + 'px'} style:--top={top + 'px'} on:click={() => { show.value = true }}>
<span class='material-symbols-outlined filled text-white'>play_arrow</span>
</button>
<img src='app.webp' alt='app' class='mw-full px-20 w-full app-image' />
<div class='overlay-gradient position-absolute top-0 left-0 w-full h-full' />
</div>
<style>
.app-image {
aspect-ratio: 2/1;
object-fit: contain;
}
.overlay-gradient {
background: linear-gradient(0deg, #101113 15.27%, rgba(16, 17, 19, 0.92) 41.28%, rgba(16, 17, 19, 0.25) 74.32%);
pointer-events: none;
}
.play-container:hover > .btn {
transition: opacity .2s ease-in-out, width .2s ease-in-out, height .2s ease-in-out, font-size .2s ease-in-out;
font-size: 30px !important;
left: clamp(0px, var(--left), 100%);
top: clamp(0px, var(--top), 100%);
width: 6rem !important;
height: 6rem !important;
}
.material-symbols-outlined {
transition: font-size .2s ease-in-out;
}
.play-container:hover .material-symbols-outlined {
font-size: 30px !important;
}
.btn {
transition: all .2s ease-in-out, width .2s ease-in-out, height .2s ease-in-out;
box-shadow: 0 0 1rem #ffffff83;
backdrop-filter: blur(8px);
top: 50%;
left: 50%;
background: none;
offset-position: center;
transform: translate(-50%, -50%);
}
</style>

View file

@ -5,6 +5,9 @@
import { throttle } from '@/modules/util.js'
import { setContext } from 'svelte'
import { writable } from 'simple-store-svelte'
import VideoModal from '$lib/components/VideoModal.svelte'
setContext('video-modal', writable(false))
const scrollPosition = writable(0)
setContext('scroll-position', scrollPosition)
@ -37,9 +40,20 @@
return deltaTime / 14
}
t.addEventListener('pointerup', () => { pos = scrollTop = scrollPosition.value = t.scrollTop })
function updateScrollPosition () {
const pos = t.scrollTop
scrollPosition.value = pos
return pos
}
t.addEventListener('scrollend', throttle(() => { scrollTop = scrollPosition.value = t.scrollTop }, 1000))
// as lightweight as possible scroll position tracking for mobile touch
t.addEventListener('touchmove', updateScrollPosition)
t.addEventListener('touchstart', () => t.removeEventListener('scroll', updateScrollPosition, { passive: true }))
t.addEventListener('touchend', () => t.addEventListener('scroll', updateScrollPosition, { passive: true }))
t.addEventListener('pointerup', () => { pos = scrollTop = updateScrollPosition() })
t.addEventListener('scrollend', throttle(() => { scrollTop = updateScrollPosition() }, 1000))
function update () {
const delta = pos - scrollTop === smooth * 2 ? 0 : ((pos - scrollTop) / smooth) * getDeltaTime()
@ -56,6 +70,7 @@
<Loader />
<div class='page-wrapper with-transitions position-relative' data-sidebar-type='overlayed-all'>
<Navbar />
<VideoModal />
<div class='overflow-x-hidden content-wrapper h-full overflow-y-scroll position-relative' use:smoothScroll>
<slot />
<Footer />

View file

@ -8,6 +8,7 @@
import WindowsSvg from '$lib/svg/WindowsSVG.svelte'
import Stargazers from '$lib/components/Stargazers.svelte'
import ShowcaseCards from '$lib/components/ShowcaseCards.svelte'
import VideoShowcase from '$lib/components/VideoShowcase.svelte'
/** @type {import('./$types').PageData} */
export let data
@ -15,10 +16,7 @@
<Hero />
<div class='container-xl'>
<div class='position-relative'>
<img src='app.webp' alt='app' class='mw-full px-20 w-full app-image' />
<div class='overlay-gradient position-absolute top-0 left-0 w-full h-full' />
</div>
<VideoShowcase />
<div class='my-20 pb-20' id='about'>
<h1 class='font-weight-bold text-white content text-center'>Torrenting made simple.</h1>
@ -54,8 +52,11 @@
</div>
</div>
<hr />
<div class='my-20 py-20'>
<div class='row overflow-hidden position-relative'>
</div>
<div class='overflow-hidden my-20 position-relative'>
<div class='container-xl'>
<div class='row'>
<div class='col-md-7 col-12 align-items-center d-flex'>
<div class='content pr-20'>
<p class='font-weight-bold text-white font-size-18'>Care to try?</p>
@ -65,13 +66,16 @@
<p class='text-muted font-size-16'>Find and download torrents, watch trailers, manage your list, search, browse and discover anime, watch together with friends and more, all in the same interface. No need to open multiple apps, tabs, everything shipped in one package.</p>
</div>
</div>
<div class='col-md-5 col-12 overflow-y-hidden overflow-y-md-unset h-600 position-relative justify-content-center justify-content-md-end d-flex'>
<div class='col-md-5 col-12 overflow-hidden overflow-md-unset h-600 position-relative justify-content-center justify-content-md-end d-flex'>
<ShowcaseCards />
<div class='overlay-gradient-top-bottom position-absolute top-0 left-0 w-full h-full z-100 d-md-none' />
</div>
<div class='overlay-gradient-top-bottom position-absolute top-0 left-0 w-full h-full z-100 d-none d-md-block' />
</div>
</div>
<div class='overlay-gradient-top-bottom position-absolute top-0 right-0 w-half h-full z-100 d-none d-md-block' />
</div>
<div class='container-xl'>
<hr />
<div class='my-20 py-20'>
<div class='row flex-column-reverse flex-md-row'>
@ -119,16 +123,8 @@
<Stargazers stargazers={data.stargazers} />
<style>
.app-image {
aspect-ratio: 2/1;
object-fit: contain;
}
.overlay-gradient-top-bottom {
background: linear-gradient(0deg, rgba(15,17,19,1) 0%, rgba(15,17,19,0) 25%, rgba(15,17,19,0) 75%, rgba(15,17,19,1) 100%);
pointer-events: none;
}
.overlay-gradient {
background: linear-gradient(0deg, #0F1113 15.27%, rgba(15, 17, 19, 0.92) 41.28%, rgba(15, 17, 19, 0.25) 74.32%);
background: linear-gradient(0deg, #101113ff 0%, #10111300 25%, #10111300 75%, #101113ff);
pointer-events: none;
}
@ -146,8 +142,8 @@
font-size: 5rem
}
@media (min-width: 769px) {
.overflow-y-md-unset {
overflow-y: unset !important;
.overflow-md-unset {
overflow: unset !important;
}
}

BIN
web/static/showcase.mp4 Normal file

Binary file not shown.