今天挑战的任务算是我蛮喜欢的一个小project,就是刻出四个slider来控制一个图片的CSS属性,包含perspective、rotateX、rotateY、rotateZ。当然这个也是参考其他教学资源(YT影片),而我另外用tailwindcss和vue composition api稍微改写。
实作逻辑
原则上不外乎就是先规划好HTML结构图,设定基础CSS样式,再来思考怎麽样让资料与模板绑定。最後再加上专案中引用到的css-doodle。让我们来看PerspectivePlayground.vue
这个档案,以下拆分成template、script、style部分。
template
原则上我会将不重复的html tag直接用tailwind写好样式,像是外层的container,会重复地方就还是会运用SCSS来写,比方说input、button。这纯属个人习惯...。
<template>
<home-btn />
<div
class="
relative
flex flex-col
items-center
justify-start
pt-28
md:justify-center
md:pt-0
font-sans
w-[100vw]
h-[100vh]
m-0
bg-[#261c33]
overflow-hidden
"
>
<h2
class="
text-[#8d81f3] text-center
font-extrabold
m-5
mt-0
text-3xl
md:text-5xl
"
>
CSS Perspective Playground
</h2>
<main
class="
flex flex-col
md:flex-row
items-center
h-[420px]
w-[600px]
font-serif
text-[20px] text-white
"
>
<section class="settings w-[50%] z-20">
<div
class="settings-container flex flex-col items-center md:items-start"
>
<!-- 将perspective绑定在label和input当中 -->
<label>perspective: {{ perspective }}px;</label>
<input type="range" min="0" max="999" v-model="perspective" />
<!-- 绑定rotateX,以此类推 -->
<label>rotateX: {{ rotateX }}deg; </label>
<input type="range" min="-180" max="180" v-model="rotateX" />
<label>rotateY: {{ rotateY }}deg; </label>
<input type="range" min="-180" max="180" v-model="rotateY" />
<label>rotateZ: {{ rotateZ }}deg; </label>
<input type="range" min="-180" max="180" v-model="rotateZ" />
</div>
<div class="btnContainer flex flex-row justify-center mb-2 md:justify-start">
<!-- 按钮部分绑定事件,会呼叫reset函式 -->
<button id="resetBtn" type="button" @click.prevent="reset">
Reset
</button>
<!-- 按钮部分绑定事件,会呼叫copy函式 -->
<button id="copyBtn" type="button" @click.prevent="copy">Copy</button>
</div>
</section>
<section class="output w-[50%] z-20">
<div class="box-container p-[50px] border-2 border-[#8d81f3]">
<div class="box" :style="box"></div>
</div>
</section>
</main>
</div>
<!-- 安装好css-doole後,可以在模板上直接使用css-doodle -->
<!-- 基本上这是照抄官方范例,就不详细解释了 -->
<css-doodle>
:doodle {
<!-- 这里使用grid和positon absolute来覆盖整个页面-->
@grid: 1x3 / 100vmax;
position: absolute;
top: 0; left: 0;
z-index: 0;
}
@size: 100% 150%;
position: absolute;
background: @m(100, (
linear-gradient(transparent, @p(
#FFFDE1@repeat(2, @p([0-9a-f])),
#FB3569@repeat(2, @p([0-9a-f]))
))
@r(0%, 100%) @r(0%, 100%) /
@r(1px) @r(23vmin)
no-repeat
));
will-change: transform;
animation: f 50s linear calc(-50s / @size() * @i()) infinite;
@keyframes f {
from { transform: translateY(-100%) }
to { transform: translateY(100%) }
}
</css-doodle>
</template>
style
将会复用的css选择器独立出来处理。
<style lang="scss" scoped>
main {
label {
color: white;
display: block;
}
input[type='range'] {
display: block;
margin-bottom: 10px;
width: 200px;
}
button {
margin-bottom: 2px;
width: 30%;
background-color: #8d81f3;
color: #fff;
font-size: 20px;
padding: 10px;
outline: none;
border: none;
margin-right: 10px;
}
.output {
.box-container {
// .box主要用来和Vue做样式绑定。
.box {
margin: auto;
width: 150px;
height: 150px;
background: #8d81f3;
}
}
}
}
</style>
script
// npm i css-doodle後,可以直接import进来,在模板上使用<css-doodle>
// 不需要另外安装vue-css-doodle,也不用额外设定config。
import 'css-doodle'
import homeBtn from '../components/HomeBtn.vue'
import { computed, ref } from 'vue'
export default {
name: '#15. CSS Perspective Playground',
components: {
homeBtn,
},
setup() {
// 建立要被绑定的资料,用ref包装起来
const perspective = ref(100)
const rotateX = ref(0)
const rotateY = ref(0)
const rotateZ = ref(0)
// 将box函式带入computed属性,只要模板上的数值有变动,就执行宣告式渲染
const box = computed(() => {
return {
transform: `
perspective(${perspective.value}px)
rotateX(${rotateX.value}deg)
rotateY(${rotateY.value}deg)
rotateZ(${rotateZ.value}deg)
`,
}
})
// 让slider的input回到预设值
const reset = () => {
// 选定DOM
const resetBtn = document.getElementById('resetBtn')
// 让DOM属性和绑定资料回到reset状态
resetBtn.innerText = 'Done!'
perspective.value = 100
rotateX.value = 0
rotateY.value = 0
rotateZ.value = 0
// 运用非同步语法,让按钮延迟0.8秒後回到原本状态
setTimeout(() => (resetBtn.innerText = 'Reset'), 800)
}
// 将当前的slider的input复制到剪贴簿上。
const copy = () => {
const copyBtn = document.getElementById('copyBtn')
copyBtn.innerText = 'Copied!'
// 建立一个DOM element <textarea>
const el = document.createElement('textarea')
// 让textarea内的文字无法修改(唯独)
el.setAttribute('readonly', '')
// 设好置入textarea内的文字
el.value = `transform: ${box.value.transform}`
// 将建立好的DOM插入到body当中
document.body.appendChild(el)
// 选择建立好的DOM
el.select()
// 执行copy,将el.value复制到剪贴簿
document.execCommand('copy')
// 复制好之後移除el
document.body.removeChild(el)
setTimeout(() => (copyBtn.innerText = 'Copy'), 800)
}
return {
perspective,
rotateX,
rotateY,
rotateZ,
box,
reset,
copy,
}
},
}
>>: 不符合成本也买 Apple Search Ads 广告的原因
Visual Studio Code(简称VS Code)是一个由微软开发的,同时支援Windows...
tags: OC 30 day 什麽是Protocol? 作用:专门用来声明一大堆方法。(不能声明属...
第 33 天:妥善运用 Heroku APP 暂存空间 我很快的把书拿了起来,翻开封面。有一行我不认...
宣告变数的资料型别--阵列 1.数值( Number ) 2.字串( String ) 3.布林值(...
笔者在开发过程中遇到一个特殊状况, 需要搜寻的索引值,j是由某个数值中的部分数值组成 组成key的元...