科幻卡片 card
簡潔、實用的科幻風格資訊容器。
終於做出我夢想中的元件了!◝(≧∀≦)◟
在學階段的專業是機電與自動控制,一直對於科幻風格的元件情有獨鍾。
過去最接近的作品為:
礙於技術力不足,可以看得出來介面非常陽春,只有勉強沾上科幻的邊。(́⊙◞౪◟⊙‵)
時隔多年,總算藉由網頁技術實現我心目中的樣子了!✧⁑。٩(ˊᗜˋ*)و✧⁕。
使用範例
基本用法
Title
The best things in life are actually really expensive.
查看範例原始碼
vue
<template>
<div class="w-full flex flex-col gap-10 border border-gray-300 p-6 pb-16">
<base-checkbox
v-model="visible"
label="顯示"
class="border rounded p-4"
/>
<div class="h-full flex justify-center">
<card-futuristic
v-on-click-outside="() => toggleSelect(false)"
:visible
:selected
class="font-orbitron"
@click="toggleSelect(true)"
>
<div class="flex flex-col gap-4">
<div class="text-xl font-bold">
Title
</div>
<div>
The best things in life are actually really expensive.
</div>
</div>
</card-futuristic>
</div>
</div>
</template>
<script setup lang="ts">
import { vOnClickOutside } from '@vueuse/components'
import { useToggle } from '@vueuse/core'
import { ref } from 'vue'
import BaseCheckbox from '../../base-checkbox.vue'
import CardFuturistic from '../card-futuristic.vue'
const visible = ref(true)
const [selected, toggleSelect] = useToggle(false)
</script>
<style lang="sass">
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+TC:[email protected]&family=Orbitron:[email protected]&family=Oxanium:[email protected]&display=swap')
.font-orbitron
font-family: "Orbitron", sans-serif
.font-oxanium
font-family: "Oxanium", sans-serif
</style>
組合零件
組合不同零件,產生多種的樣式。
COD-00
FUTURISTIC CARD
CARD-01
QUOTE CORNER
CARD-02
SIDE BORDER CLIP
CARD-03
SOLID BACKGROUND
ERROR
FISH OVERWEIGHT
COD
查看範例原始碼
vue
<template>
<div class="w-full flex flex-col gap-4 border border-gray-300 p-6">
<base-checkbox
v-model="visible"
label="顯示"
class="sticky top-20 z-10 border rounded bg-white p-4 md:relative md:top-0"
/>
<div class="flex flex-wrap items-center justify-center gap-10">
<card-futuristic
v-for="item, i in list"
:key="i"
v-on-click-outside="() => item.selected = false"
v-bind="item"
class="font-orbitron"
@click="item.selected = true"
>
<div class="flex flex-col">
<div
v-if="item.title"
:class="item.titleClass"
>
{{ item.title }}
</div>
<div
v-if="item.text"
:class="item.textClass"
>
{{ item.text }}
</div>
</div>
</card-futuristic>
</div>
</div>
</template>
<script setup lang="ts">
import type { Writable } from 'type-fest'
import type { ExtractComponentProps } from '../../../types'
import { vOnClickOutside } from '@vueuse/components'
import { promiseTimeout } from '@vueuse/core'
import { map, pipe } from 'remeda'
import { ref, watch } from 'vue'
import BaseCheckbox from '../../base-checkbox.vue'
import CardFuturistic from '../card-futuristic.vue'
type CardProp = Writable<ExtractComponentProps<typeof CardFuturistic>> & {
title?: string;
titleClass?: string;
text?: string;
textClass?: string;
}
const list = ref(pipe(
[
{
title: 'COD-00',
titleClass: 'text-xl font-bold',
text: 'FUTURISTIC CARD',
bg: { type: 'halftone' },
border: {
type: 'specific',
selectedColor: '#FFF',
strokeWidth: 1,
side: {
t: {},
l: {},
b: {},
r: {},
},
},
animeSequence: {
visible: {
border: { duration: 400 },
},
},
},
{
title: 'CARD-01',
titleClass: 'text-xl font-bold',
text: 'QUOTE CORNER',
corner: { type: 'quote' },
content: {
type: 'scale',
class: 'p-4',
},
bg: { type: 'halftone' },
border: null,
animeSequence: {
visible: {
content: { delay: 200 },
bg: { delay: 400 },
},
},
},
{
title: 'CARD-02',
titleClass: 'text-xl font-bold ',
text: 'SIDE BORDER CLIP',
textClass: '',
corner: null,
content: {
type: 'clip',
class: 'p-4',
},
bg: {
type: 'typical',
margin: '0',
},
border: { type: 'side' },
animeSequence: {
normal: {
border: { delay: 0 },
},
visible: {
border: { delay: 0 },
bg: { delay: 200 },
content: { delay: 300 },
},
hidden: {
border: { delay: 300 },
bg: { delay: 0 },
content: { delay: 0 },
},
},
},
{
title: 'CARD-03',
titleClass: 'text-xl font-bold text-white',
text: 'SOLID BACKGROUND',
textClass: 'text-white',
corner: null,
bg: {
type: 'solid',
selectedColor: '#444',
},
content: {
type: 'typical',
class: 'p-4 pl-6',
},
border: { type: 'specific' },
animeSequence: {
visible: {
border: { delay: 400 },
},
},
},
{
title: 'ERROR',
titleClass: 'text-2xl font-bold text-[#ba2507] ',
text: 'FISH OVERWEIGHT',
textClass: 'text-[#ba2507]',
corner: null,
bg: {
type: 'typical',
margin: '4px 0px',
color: '#ffe8e8',
},
content: {
type: 'typical',
class: 'py-4 px-8',
},
border: {
type: 'specific',
color: '#ba2507',
selectedColor: '#f07860',
strokeWidth: 2,
side: {
t: {},
b: {},
},
},
animeSequence: {
normal: {
border: { delay: 0 },
},
visible: {
border: { delay: 0 },
bg: { delay: 400 },
content: { delay: 500 },
},
hidden: {
border: { delay: 300 },
bg: { delay: 0 },
content: { delay: 0 },
},
// null 表示停用動畫
selected: { content: null },
hover: { content: null },
},
},
{
title: 'COD',
corner: {
type: 'square',
size: 2,
},
bg: null,
content: {
type: 'typical',
class: 'p-1 px-2',
},
border: {
type: 'typical',
color: '#BBB',
},
animeSequence: {
visible: {
corner: { delay: 0 },
border: { delay: 400 },
content: { delay: 500 },
},
hidden: {
corner: { delay: 400 },
border: { delay: 0 },
content: { delay: 0 },
},
},
},
] as CardProp[],
map((data) => ({
...data,
visible: false,
selected: false,
})),
))
const visible = ref(false)
watch(visible, async (value) => {
for (const data of list.value) {
data.visible = value
await promiseTimeout(100)
}
})
</script>
<style lang="sass">
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+TC:[email protected]&family=Orbitron:[email protected]&family=Oxanium:[email protected]&display=swap')
.font-orbitron
font-family: "Orbitron", sans-serif
.font-oxanium
font-family: "Oxanium", sans-serif
</style>
文字動畫
使用文件元件就可以產生科幻文字特效。
Futuristic Card
此元件主要由 border、bg、corner、content 與 ornament 子元件組成,由 card 父元件負責調度動畫。
子元件可以任意組合,藉此產生 N 種有趣的樣式設計。
使用 Vue 綁定 SVG 的 Attr 進行繪圖,並使用 anime.js 實現動畫
查看範例原始碼
vue
<template>
<div class="w-full flex flex-col gap-4 border border-gray-300 p-6">
<base-checkbox
v-model="visible"
label="顯示"
class="sticky top-20 z-10 border rounded bg-white p-4 md:relative md:top-0"
/>
<div class="flex flex-wrap items-center justify-center gap-20">
<card-futuristic
v-for="item, i in list"
:key="i"
v-on-click-outside="() => item.selected = false"
v-bind="item"
class="font-orbitron"
@click="item.selected = true"
>
<div class="flex flex-col gap-2">
<card-futuristic-text
v-if="item.title"
:text="item.title"
:class="item.titleClass"
class="!m-0"
/>
<card-futuristic-text
v-for="line, j in item.text"
:key="j"
:text="line"
:class="item.textClass"
class="!m-0"
/>
</div>
</card-futuristic>
</div>
</div>
</template>
<script setup lang="ts">
import type { Writable } from 'type-fest'
import type { ExtractComponentProps } from '../../../types'
import { vOnClickOutside } from '@vueuse/components'
import { promiseTimeout } from '@vueuse/core'
import { map, pipe } from 'remeda'
import { ref, watch } from 'vue'
import BaseCheckbox from '../../base-checkbox.vue'
import CardFuturistic from '../card-futuristic.vue'
import CardFuturisticText from '../card-futuristic-text.vue'
type CardProp = Writable<ExtractComponentProps<typeof CardFuturistic>> & {
title?: string;
titleClass?: string;
text?: string[];
textClass?: string;
}
const list = ref(pipe(
[
{
title: 'Futuristic Card',
titleClass: 'text-xl font-bold',
text: [
`此元件主要由 border、bg、corner、content 與 ornament 子元件組成,由 card 父元件負責調度動畫。`,
`子元件可以任意組合,藉此產生 N 種有趣的樣式設計。`,
`使用 Vue 綁定 SVG 的 Attr 進行繪圖,並使用 anime.js 實現動畫`,
],
bg: {
type: 'halftone',
color: '#0001',
dotSize: '1px',
size: '10px',
},
corner: {
type: 'quote',
strokeWidth: 8,
},
border: {
type: 'typical',
color: '#777',
},
animeSequence: {
visible: {
corner: { delay: 0 },
bg: { delay: 400 },
border: { delay: 400 },
content: { delay: 500 },
},
hidden: {
corner: { delay: 700 },
bg: { delay: 200 },
border: { delay: 100 },
content: { delay: 100 },
},
hover: {
corner: null,
border: null,
content: null,
},
selected: {
corner: null,
border: null,
content: null,
},
},
},
] as CardProp[],
map((data) => ({
...data,
visible: false,
selected: false,
})),
))
const visible = ref(false)
watch(visible, async (value) => {
for (const data of list.value) {
data.visible = value
await promiseTimeout(100)
}
})
</script>
<style lang="sass">
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+TC:[email protected]&family=Orbitron:[email protected]&family=Oxanium:[email protected]&display=swap')
.font-orbitron
font-family: "Orbitron", sans-serif
.font-oxanium
font-family: "Oxanium", sans-serif
</style>
原理
動態由 border、bg、corner、content 之子元件構成,由 card 父元件負責調度動畫。
子元件可以任意組合,藉此產生 N 種有趣的樣式設計。
子元件具體實作使用 Vue 綁定 SVG 的 Attr 進行繪圖,並使用 anime.js 實現動畫
原始碼
API
Props
interface Props {
/** 動畫序列,可自定義動畫參數 */
animeSequence?: Partial<AnimeSequence>;
visible?: boolean;
selected?: boolean;
/** 為空則自動處理,有提供則以參數數值為主 */
hover?: boolean;
/** null 表示不使用此元件 */
border?: BorderParam | null;
bg?: BgParam | null;
corner?: CornerParam | null;
content?: ContentParam | null;
}
Methods
defineExpose({
/** 執行特定 part 狀態動畫 */
execute,
})
Slots
defineSlots<{
default?: () => unknown;
}>()