Разделил на компоненты
All checks were successful
Gitea Actions Demo / build-and-deploy (push) Successful in 25s
All checks were successful
Gitea Actions Demo / build-and-deploy (push) Successful in 25s
This commit is contained in:
parent
250174211e
commit
e823d9c322
77
app.vue
77
app.vue
@ -4,23 +4,14 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div v-for="card in cards" ref="cardsELS" :key="card.title" class="card">
|
<AppCard v-for="card in cards" ref="cardEls" :key="card.title" v-bind="card" />
|
||||||
<p class="card__title">
|
|
||||||
{{ card.title }}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<img :src="card.img" alt="TeamSpeak" class="card__image">
|
|
||||||
|
|
||||||
<a class="button card__action" :href="card.href">
|
|
||||||
{{ card.action }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { gsap } from 'gsap'
|
import { gsap } from 'gsap'
|
||||||
|
import AppCard from '~/components/app-card.vue'
|
||||||
|
|
||||||
const cards = ref([
|
const cards = ref([
|
||||||
{
|
{
|
||||||
@ -55,7 +46,7 @@ const cards = ref([
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const cardsELS = ref([])
|
const cardEls = ref([])
|
||||||
const logoEl = ref()
|
const logoEl = ref()
|
||||||
|
|
||||||
const DURATION = 0.3
|
const DURATION = 0.3
|
||||||
@ -70,20 +61,19 @@ const CARD_OPTIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const cards = cardsELS.value
|
const groupsCount = Math.ceil((cardEls.value.length - 1) / 2)
|
||||||
const groupsCount = Math.ceil((cards.length - 1) / 2)
|
|
||||||
|
|
||||||
const t = gsap.timeline()
|
const t = gsap.timeline()
|
||||||
|
|
||||||
if (cards[0])
|
if (cardEls.value[0])
|
||||||
t.to(cards[0], CARD_OPTIONS)
|
t.to(cardEls.value[0].$el, CARD_OPTIONS)
|
||||||
|
|
||||||
for (let i = 0; i < groupsCount; i++) {
|
for (let i = 0; i < groupsCount; i++) {
|
||||||
if (cards[i * 2 + 1])
|
if (cardEls.value[i * 2 + 1])
|
||||||
t.to(cards[i * 2 + 1], CARD_OPTIONS, `<${OVERLAP}`)
|
t.to(cardEls.value[i * 2 + 1].$el, CARD_OPTIONS, `<${OVERLAP}`)
|
||||||
|
|
||||||
if (cards[i * 2 + 2])
|
if (cardEls.value[i * 2 + 2])
|
||||||
t.to(cards[i * 2 + 2], CARD_OPTIONS, `<<${OVERLAP}`)
|
t.to(cardEls.value[i * 2 + 2].$el, CARD_OPTIONS, `<<${OVERLAP}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.fromTo(
|
t.fromTo(
|
||||||
@ -124,51 +114,4 @@ header {
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr auto;
|
|
||||||
align-items: center;
|
|
||||||
justify-items: flex-start;
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 16px;
|
|
||||||
gap: 16px;
|
|
||||||
background-image: linear-gradient(135deg, var(--chocolate-cosmos), var(--black));
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__image {
|
|
||||||
width: 80px;
|
|
||||||
grid-row: span 2;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__action {
|
|
||||||
grid-row: 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
display: inline-block;
|
|
||||||
text-decoration: none;
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
background-image: linear-gradient(135deg, var(--beaver) 0%, var(--beaver) 51%, var(--walnut-brown) 110%);
|
|
||||||
background-size: 200% auto;
|
|
||||||
background-color: var(--walnut-brown);
|
|
||||||
background-repeat: repeat;
|
|
||||||
color: var(--white-smoke);
|
|
||||||
font-weight: 500;
|
|
||||||
transition: .15s ease-out;
|
|
||||||
transition-property: color, background-position;
|
|
||||||
|
|
||||||
&:hover:not(:active) {
|
|
||||||
background-position: right center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
53
components/app-button.vue
Normal file
53
components/app-button.vue
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<Component :is="tag" class="app-button" v-bind="attrs">
|
||||||
|
<slot />
|
||||||
|
</Component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'AppButton',
|
||||||
|
})
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
href?: string
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const isLink = computed(() => !!props.href)
|
||||||
|
const tag = computed(() => isLink.value ? 'a' : 'button')
|
||||||
|
const attrs = computed(() => {
|
||||||
|
if (isLink.value) {
|
||||||
|
return {
|
||||||
|
href: props.href,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {
|
||||||
|
type: 'button',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.app-button {
|
||||||
|
display: inline-block;
|
||||||
|
text-decoration: none;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
background-image: linear-gradient(135deg, var(--beaver) 0%, var(--beaver) 51%, var(--walnut-brown) 110%);
|
||||||
|
background-size: 200% auto;
|
||||||
|
background-color: var(--walnut-brown);
|
||||||
|
background-repeat: repeat;
|
||||||
|
color: var(--white-smoke);
|
||||||
|
font-weight: 500;
|
||||||
|
transition: .15s ease-out;
|
||||||
|
transition-property: color, background-position;
|
||||||
|
|
||||||
|
&:hover:not(:active) {
|
||||||
|
background-position: right center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
54
components/app-card.vue
Normal file
54
components/app-card.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div class="app-card">
|
||||||
|
<p class="app-card__title">
|
||||||
|
{{ title }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<img :src="img" alt="TeamSpeak" class="app-card__image">
|
||||||
|
|
||||||
|
<AppButton class="app-card__action" :href="href">
|
||||||
|
{{ action }}
|
||||||
|
</AppButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineOptions({
|
||||||
|
name: 'AppCard',
|
||||||
|
})
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
title: string
|
||||||
|
action: string
|
||||||
|
href: string
|
||||||
|
img: string
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.app-card {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: flex-start;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
gap: 16px;
|
||||||
|
background-image: linear-gradient(135deg, var(--chocolate-cosmos), var(--black));
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__image {
|
||||||
|
width: 80px;
|
||||||
|
grid-row: span 2;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__action {
|
||||||
|
grid-row: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user