111
This commit is contained in:
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 411 KiB After Width: | Height: | Size: 411 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
82
src/components/email-box.vue
Normal file
82
src/components/email-box.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="email-box">
|
||||
<h3 class="title">{{ title }}</h3>
|
||||
<div class="tip">{{ tip }}</div>
|
||||
<input
|
||||
v-model="email"
|
||||
@keydown.enter.prevent="submit"
|
||||
type="email"
|
||||
:placeholder="$t('EmailAddress')"
|
||||
/>
|
||||
<div v-show="error" class="error">{{ error }}</div>
|
||||
<button custom @click="submit">{{ $t('Submit') }}</button>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
const emit = defineEmits(['submit'])
|
||||
const props = defineProps({
|
||||
title: { type: String },
|
||||
tip: { type: String }
|
||||
})
|
||||
const email = ref('')
|
||||
const error = ref('')
|
||||
const submit = () => {
|
||||
if (!validateEmail(email.value)) return
|
||||
emit('submit', email.value)
|
||||
}
|
||||
// 验证邮箱
|
||||
const validateEmail = (email: string) => {
|
||||
if (email === '') {
|
||||
error.value = 'Please fill out this field.'
|
||||
return false
|
||||
} else if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)) {
|
||||
error.value = 'Please enter a valid email address.'
|
||||
return false
|
||||
}
|
||||
error.value = ''
|
||||
return true
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.email-box {
|
||||
width: 100%;
|
||||
padding: 80px;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 0px 40px 0px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 40px;
|
||||
> .title {
|
||||
font-size: 40px;
|
||||
color: #222;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
> .tip {
|
||||
color: #4d4d4d;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
> input {
|
||||
width: 100%;
|
||||
border-radius: 40px;
|
||||
height: 40px;
|
||||
padding: 0 20px;
|
||||
border: 1px solid #e1e1e1;
|
||||
outline: none;
|
||||
color: #222;
|
||||
}
|
||||
> .error {
|
||||
font-size: 14px;
|
||||
color: #dc3232;
|
||||
}
|
||||
> button {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
border-radius: 50px;
|
||||
height: 50px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
--hover-backcolor: #000;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -204,18 +204,18 @@ function setDocumentStyles(parent, el, p = 0) {
|
||||
`opacity ${oDuration} ${oDelay} ${oEasing}`,
|
||||
]
|
||||
el.style.transition = transitionArr.join(', ')
|
||||
const tX = getCurrentValue(el, T.translateX_s, T.translateX, p)
|
||||
const tY = getCurrentValue(el, T.translateY_s, T.translateY, p)
|
||||
const sx = getCurrentValue(el, T.scaleX_s, T.scaleX, p, T.scale_s, T.scale, 1)
|
||||
const sy = getCurrentValue(el, T.scaleY_s, T.scaleY, p, T.scale_s, T.scale, 1)
|
||||
const r = getCurrentValue(el, T.rotate_s, T.rotate, p)
|
||||
const rx = getCurrentValue(el, T.rotateX_s, T.rotateX, p)
|
||||
const ry = getCurrentValue(el, T.rotateY_s, T.rotateY, p)
|
||||
const rz = getCurrentValue(el, T.rotateZ_s, T.rotateZ, p)
|
||||
const transform = `translate(${tX}px, ${tY}px) scale(${sx}, ${sy}) rotate(${r}deg) rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`
|
||||
const { num: tX, unit: tXUnit } = getCurrentValue(el, T.translateX_s, T.translateX, p)
|
||||
const { num: tY, unit: tYUnit } = getCurrentValue(el, T.translateY_s, T.translateY, p)
|
||||
const { num: sx } = getCurrentValue(el, T.scaleX_s, T.scaleX, p, T.scale_s, T.scale, 1)
|
||||
const { num: sy } = getCurrentValue(el, T.scaleY_s, T.scaleY, p, T.scale_s, T.scale, 1)
|
||||
const { num: r } = getCurrentValue(el, T.rotate_s, T.rotate, p)
|
||||
const { num: rx } = getCurrentValue(el, T.rotateX_s, T.rotateX, p)
|
||||
const { num: ry } = getCurrentValue(el, T.rotateY_s, T.rotateY, p)
|
||||
const { num: rz } = getCurrentValue(el, T.rotateZ_s, T.rotateZ, p)
|
||||
const transform = `translate(${tX}${tXUnit || 'px'}, ${tY}${tYUnit || 'px'}) scale(${sx}, ${sy}) rotate(${r}deg) rotateX(${rx}deg) rotateY(${ry}deg) rotateZ(${rz}deg)`
|
||||
el.style.transform = transform
|
||||
if (hasAttr(el, [T.opacity_s, T.opacity])) {
|
||||
el.style.opacity = getCurrentValue(el, T.opacity_s, T.opacity, p, T.opacity_s, T.opacity, 1)
|
||||
el.style.opacity = getCurrentValue(el, T.opacity_s, T.opacity, p, T.opacity_s, T.opacity, 1).num
|
||||
}
|
||||
}
|
||||
function getAttrs(el, attrs = {}) {
|
||||
@@ -227,9 +227,16 @@ function getAttrs(el, attrs = {}) {
|
||||
return obj
|
||||
}
|
||||
function getCurrentValue(el, start, end, progress, bStart, bEnd, defaultValue = 0) {
|
||||
const startNum = hasAttr(el, start) ? Number(el.getAttribute(start)) : hasAttr(el, bStart) ? Number(el.getAttribute(bStart)) : defaultValue
|
||||
const endNum = hasAttr(el, end) ? Number(el.getAttribute(end)) : hasAttr(el, bEnd) ? Number(el.getAttribute(bEnd)) : defaultValue
|
||||
return startNum + (endNum - startNum) * progress
|
||||
const startStr = hasAttr(el, start) ? el.getAttribute(start) : hasAttr(el, bStart) ? el.getAttribute(bStart) : String(defaultValue)
|
||||
const endStr = hasAttr(el, end) ? el.getAttribute(end) : hasAttr(el, bEnd) ? el.getAttribute(bEnd) : String(defaultValue)
|
||||
const startNum = parseInt(startStr)
|
||||
const endNum = parseInt(endStr)
|
||||
const starUnit = startStr.match(/(px|deg|%|rem|em|vh|vw|pt|pc|mm|cm|in)$/i)?.[1] || ''
|
||||
const endUnit = endStr.match(/(px|deg|%|rem|em|vh|vw|pt|pc|mm|cm|in)$/i)?.[1] || ''
|
||||
return {
|
||||
num: startNum + (endNum - startNum) * progress,
|
||||
unit: starUnit || endUnit
|
||||
}
|
||||
}
|
||||
function hasAttr(el, attr) {
|
||||
if (Array.isArray(attr)) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="contact-us">
|
||||
<img class="bg" src="@/assets/images/contact-us/bg.jpg" alt="" />
|
||||
<section class="header">
|
||||
<h1 v-custom-animation.once duration="1s" translate-y-s="-100" opacity-s="0">
|
||||
<h1 v-custom-animation.once duration="1s" translate-y-s="-100%" opacity-s="0">
|
||||
{{ $t('MainHeader.ContactUs') }}
|
||||
</h1>
|
||||
</section>
|
||||
@@ -12,40 +12,16 @@
|
||||
<a class="email" href="mailto:info@code-create.com.hk">info@code-create.com.hk</a>
|
||||
</section>
|
||||
<section class="contact-input">
|
||||
<div class="box">
|
||||
<h3 class="title">{{ $t('GetInTouch') }}</h3>
|
||||
<div class="tip">{{ $t('StayUpToDate') }}</div>
|
||||
<input
|
||||
v-model="email"
|
||||
@keydown.enter.prevent="submit"
|
||||
type="email"
|
||||
:placeholder="$t('EmailAddress')"
|
||||
/>
|
||||
<div v-show="error" class="error">{{ error }}</div>
|
||||
<button custom @click="submit">{{ $t('Submit') }}</button>
|
||||
</div>
|
||||
<EmailBox @submit="submit" :title="$t('GetInTouch')" :tip="$t('StayUpToDate')" />
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
const email = ref('')
|
||||
const error = ref('')
|
||||
const submit = () => {
|
||||
if (!validateEmail(email.value)) return
|
||||
console.log(email.value)
|
||||
}
|
||||
// 验证邮箱
|
||||
const validateEmail = (email: string) => {
|
||||
if (email === '') {
|
||||
error.value = 'Please fill out this field.'
|
||||
return false
|
||||
} else if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)) {
|
||||
error.value = 'Please enter a valid email address.'
|
||||
return false
|
||||
}
|
||||
error.value = ''
|
||||
return true
|
||||
import EmailBox from '@/components/email-box.vue'
|
||||
|
||||
const submit = (email: string) => {
|
||||
console.log(email)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -104,44 +80,8 @@
|
||||
> .contact-input {
|
||||
padding: 100px;
|
||||
background-color: #faf8f8;
|
||||
> .box {
|
||||
> .email-box {
|
||||
max-width: 860px;
|
||||
width: 100%;
|
||||
padding: 80px;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
box-shadow: 0px 0px 40px 0px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 40px;
|
||||
> .title {
|
||||
font-size: 40px;
|
||||
color: #222;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
> .tip {
|
||||
color: #4d4d4d;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
> input {
|
||||
width: 100%;
|
||||
border-radius: 40px;
|
||||
height: 40px;
|
||||
padding: 0 20px;
|
||||
border: 1px solid #e1e1e1;
|
||||
outline: none;
|
||||
color: #222;
|
||||
}
|
||||
> .error {
|
||||
font-size: 14px;
|
||||
color: #dc3232;
|
||||
}
|
||||
> button {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
border-radius: 50px;
|
||||
height: 50px;
|
||||
font-weight: bold;
|
||||
--hover-backcolor: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="media">
|
||||
<img class="bg" src="@/assets/images/media/bg.jpg" alt="" />
|
||||
<section class="header">
|
||||
<h1 v-custom-animation.once duration="1s" translate-y-s="-100" opacity-s="0">
|
||||
<h1 v-custom-animation.once duration="1s" translate-y-s="-100%" opacity-s="0">
|
||||
{{ $t('MainHeader.Media') }}
|
||||
</h1>
|
||||
</section>
|
||||
|
||||
179
src/pages/mixi/index.vue
Normal file
179
src/pages/mixi/index.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div class="mixi">
|
||||
<img class="bg" src="@/assets/images/mixi/bg.jpg" alt="" />
|
||||
<section class="header">
|
||||
<img
|
||||
src="@/assets/images/mixi/logo-mixi.png"
|
||||
alt=""
|
||||
v-custom-animation.once
|
||||
duration="1s"
|
||||
translate-y-s="100%"
|
||||
opacity-s="0"
|
||||
/>
|
||||
</section>
|
||||
<section class="introduce">
|
||||
<div class="box" v-custom-animation.once="{ duration: '1.25s' }">
|
||||
<h2 class="title">
|
||||
Best-in-class Precise Fashion Attribute and Colour Recognition System
|
||||
</h2>
|
||||
<p class="tip" translate-y-s="100%" opacity-s="0">
|
||||
Mixi is an AI-based fine-grained fashion attribute and colour recognition tool
|
||||
that can be used for both online shopping platforms and bricks and mortar
|
||||
stores. Mixi allows the customer to easily find fashion items that possess
|
||||
his/her preferred fashion attributes and colours.
|
||||
</p>
|
||||
<button custom @click="scrollToJoin" translate-y-s="100%" opacity-s="0">
|
||||
CONTACT US
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="video">
|
||||
<div class="content" @click="playVideo">
|
||||
<img
|
||||
src="https://code-create.com.hk/wp-content/uploads/2022/11/mixi_video_thumb.jpg"
|
||||
alt=""
|
||||
/>
|
||||
<span class="iconfont icon-bofang"></span>
|
||||
</div>
|
||||
</section>
|
||||
<section class="email-input" ref="joinRef">
|
||||
<EmailBox
|
||||
@submit="submit"
|
||||
:title="$t('Interested in Mixi?')"
|
||||
:tip="$t('We will contact you for customized plan.')"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import EmailBox from '@/components/email-box.vue'
|
||||
import MyEvent from '@/directives/myEvents'
|
||||
|
||||
const playVideo = () => {
|
||||
MyEvent.emit('playVideo', {
|
||||
url: 'https://code-create.com.hk/wp-content/uploads/2022/11/mixi_video.mp4',
|
||||
poster: 'https://code-create.com.hk/wp-content/uploads/2022/11/mixi_video_thumb.jpg'
|
||||
})
|
||||
}
|
||||
const joinRef = ref(null)
|
||||
const scrollToJoin = () => {
|
||||
window.scrollTo({
|
||||
top: joinRef.value.offsetTop,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
const submit = (email: string) => {
|
||||
console.log(email)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.mixi {
|
||||
> * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
> .bg {
|
||||
height: 500px;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
> .header {
|
||||
height: 500px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: transparent;
|
||||
> img {
|
||||
max-width: 200px;
|
||||
width: 90%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
> .introduce {
|
||||
padding: 200px 0;
|
||||
background-color: #eeeeee;
|
||||
> .box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 730px;
|
||||
text-align: center;
|
||||
> .title {
|
||||
color: #222;
|
||||
font-size: 24px;
|
||||
margin-bottom: 30px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
> .tip {
|
||||
color: #555;
|
||||
font-size: 16px;
|
||||
line-height: 25px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
> button {
|
||||
width: auto;
|
||||
height: auto;
|
||||
border-radius: 40px;
|
||||
padding: 20px 40px;
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .video {
|
||||
width: 100%;
|
||||
background-color: #463a37;
|
||||
padding: 100px 0;
|
||||
> .content {
|
||||
max-width: 1120px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
> img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
> .icon-bofang {
|
||||
font-size: 100px;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
animation: identifier 2s ease-in-out infinite;
|
||||
transition: 0.3s all;
|
||||
@keyframes identifier {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
filter: drop-shadow(0px 0px 8px rgba(255, 255, 255, 1));
|
||||
}
|
||||
50% {
|
||||
transform: translate(-50%, -50%) scale(0.95);
|
||||
filter: drop-shadow(0px 0px 0px rgba(255, 255, 255, 1));
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
filter: drop-shadow(0px 0px 8px rgba(255, 255, 255, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
> .email-input {
|
||||
padding: 100px;
|
||||
> .email-box {
|
||||
max-width: 860px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -37,6 +37,11 @@ export const routes: RouteRecordRaw[] = [
|
||||
name: 'Aida',
|
||||
component: () => import('./pages/aida/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'mixi',
|
||||
name: 'mixi',
|
||||
component: () => import('./pages/mixi/index.vue')
|
||||
},
|
||||
{ path: 'events',
|
||||
name: 'events',
|
||||
component: () => import('./pages/events/index.vue')
|
||||
|
||||
Reference in New Issue
Block a user