点名动效
知识点Attr()
attr() 函数返回选择元素的属性值。
- CSS函数attr()是用来获取被选中元素的属性值,并且在样式文件中使用。它也可以用在伪类元素里,在伪类元素里使用,它得到的是伪元素的原始元素的值。
- attr()函数可以和任何CSS属性一起使用,但是除了content外,其余都还是试验性的(简单说就是不稳定,浏览器不一定支持)
代码
<a href="#" class="btn" data-tip="点击作答">一个按钮</a>
.btn::before {
content: attr(data-tip);
width: 80px;
padding: 5px 10px;
border-radius: 4px;
background-color: #000;
color: #ccc;
position: absolute;
top: -30px;
left: 50%;
transform: translate(-50%);
text-align: center;
opacity: 0;
transition: all .3s;
}
<!--
* @Author: yangyuguang
* @Date: 2022-10-09 15:11:29
* @LastEditors: yangyuguang
* @LastEditTime: 2022-10-09 21:00:37
* @FilePath: /my_test/src/components/www.vue
-->
<template>
<div class="list"></div>
</template>
<script>
export default {
data () {
return {
list: '',
randomSurnam: [
'赵',
'钱',
'孙',
'李',
'周',
'吴',
'郑',
'王',
'冯',
'陈',
'褚',
'卫',
'蒋',
'沈',
'韩',
'杨'
],
randomName: [
'泽',
'桐',
'梓',
'一',
'宁',
'梅',
'生',
'平',
'明',
'涵',
'韵',
'宸',
'坤',
'华',
'星',
'家'
],
names: [],
run: true,
deg: 0,
slow: 0
}
},
methods: {
init () {
for (let i = 0; i < 40; i++) {
this.names.push(
this.randomSurnam[(Math.random() * this.randomSurnam) | 0] +
this.randomName[Math.random() * this.randomName.length|0] +
this.randomName[Math.random() * this.randomName.length|0]
)
}
for (let key in this.names) {
let span = document.createElement('span')
span.innerText = this.names[key]
this.list.appendChild(span)
}
this.animate()
this.event()
},
event () {
document.addEventListener('keyup', (e) => {
console.log('====================================');
console.log(e);
console.log('====================================');
if (e.code == 'Space') {
this.run = !this.run
}
})
},
draw () {
const spans = this.list.children
for (let i = 0; i < spans.length; i++) {
let span = spans[i]
span.style.transform = `rotate(${i / this.names.length * 360 +
this.deg}deg)`;
span.style.color = '#fff'
}
const now =((360 - this.deg) / 360 * this.names.length| 0) % this.names.length
this.list.setAttribute('data', this.names[now])
spans[now].style.color = 'red'
if (this.run) {
this.slow = 1
} else {
this.slow = this.slow * 0.995
}
this.deg -= this.slow
},
animate(){
setInterval(()=>this.draw(),10)
}
},
mounted () {
this.list = document.querySelector('.list')
this.init()
}
}
</script>
<style scoped>
.list {
width: 400px;
height: 400px;
border-radius: 50%;
border: 1px solid #fff;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.list::after {
content: '';
display: absolute;
width: 0;
height: 0;
right: 0;
border-width: 6px;
border-style: solid;
border-color: transparent transparent transparent red;
}
.list::before {
content: attr(data);
position: absolute;
color: #1e90ff;
font-size: 64px;
}
.list span {
color: #fff;
position: absolute;
width: calc(100% + 120px);
height: 20px;
text-align: right;
transition: 0.01 linear;
user-select: none;
}
</style>
效二
知识点
- 下面代码中大量使用 在js中的使用setProperty设置变量 在css中利用var使用
- perspective 属性指定了观察者与z=0平面的距离,使具有三维位置变换的元素产生透视效果
- 把需要切换的颜色都提出来用css变量代替,并添加上过渡效果 在不同主题类名里修改css变量
- transitions 可以决定哪些属性发生动画效果 (明确地列出这些属性),何时开始 (设置 delay),持续多久 (设置 duration) 以及如何动画 (定义timing funtion,比如匀速地或先快后慢)。
- padding:inherit; //内边距继承父元素
代码
<template>
<div id="btnWrapper" ref="btnWrapper">
<div class="btn active">按钮1</div>
<div class="btn">按钮2</div>
</div>
</template>
<script>
export default {
mounted () {
let that = this
let wrapper = this.$refs.btnWrapper
// 先给容器设置一个css变量并附初始值
wrapper.style.setProperty('--groove-left', '12px')
// 获取按钮元素
let btns = wrapper.children
for (let i = 0; i < btns.length; i++) {
// 给每个按钮添加点击事件
btns[i].addEventListener('click', function (e) {
// 点击后,修改css变量的值
wrapper.style.setProperty('--groove-left', `calc(12px + ${i * 50}%)`)
wrapper.style.setProperty('--wrapper-rotate', `${i===0?-8:8}deg`)
wrapper.style.setProperty('--wraper-origin', `${i===0? '80% top':'20% top'}`)
wrapper.className = "rotateWrap"
setTimeout(()=>{
wrapper.className = ""
},500)
that.themeChange(i==1)
// 初始化所有按钮
that.resetBtn(btns)
setTimeout(()=>{
btns[i].className = "btn active"
},500)
})
}
},
methods: {
resetBtn(btns) {
for(let i=0;i<btns.length;i++){
setTimeout(()=>{
btns[i].className = "btn"
},100)
}
},
themeChange(bol){
let body = document.body
body.className = bol?'dark':""
}
},
}
</script>
<style scoped>
#btnWrapper {
position: relative;
margin:300px auto 0;
width: 380px;
height: 80px;
padding: 12px 16px;
border-radius: 12px;
overflow: hidden;
background-color: var(--c-wrap-bg);;
box-shadow: -10px -10px 15px var(--c-wrap-shadow1), 10px 10px 15px var(--c-wrap-shadow2);
/* transform-origin:center; */
transform-origin:var(--wraper-origin);
/* 添加过渡效果 */
transition: transform 0.4s cubic-bezier(0,0,0.48,1);
}
.rotateWrap{
/* 添加y轴旋转 */
transform: rotateY(var(--wrapper-rotate));
}
.btn {
float: left;
display: flex;
width: 50%;
height: 100%;
align-items: center;
justify-content: center;
padding: inherit;
color: #aaa;
cursor: pointer;
/* 添加文字颜色过度效果 */
transition: color 0.4s linear;
/* 添加按钮未选中动画 注意时间 */
animation: txtOutScale 0.6s linear;
}
.active {
color: #111;
/* 添加文字颜色过度效果*/
transform: scale(1.1);
/* 添加按钮未选中动画 注意时间 */
animation:txtEnterScale 0.4s linear;
}
#btnWrapper::before {
content: '';
position: absolute;
left: var(--groove-left);
top: 12px;
width: calc(50% - 16px - 10px);
height: calc(100% - 24px);
border-radius: 12px;
box-shadow: inset 8px 8px 6px var(--c-btn-shadow1), inset -5px -5px 15px var(--c-btn-shadow2),
inset -5px -5px 15px var(--c-btn-shadow2), inset 7px 7px 6px var(--c-btn-shadow1);
transition: left 1s cubic-bezier(0.82, 0.12, 0.18, 0.88),box-shadow 0.4s linear;
}
/* 按钮选中缩放动画 */
@keyframes txtEnterScale {
0%{
transform: scale(1);
}
80%{
transform: scale(1.15);
}
100%{
transform: scale(1.1);
}
}
/* 按钮未选中缩放动画 */
@keyframes textOutScale {
0%{
transform: scale(1.1);
}
80%{
transform: scale(0.95);
}
100%{
transform: scale(1);
}
}
</style>
html中设置
*{
margin:0;
padding:0;
box-sizing: border-box;
}
body{
perspective: 500px;
background-color: #edf1f4;
transition: background-color 0.4s linear;
--c-wrap-shadow1: #f5f9fd;
--c-wrap-shadow2: #d8dbe5;
--c-wrap-bg: #e2e6eb;
--c-btn-shadow1: #d9dbe6;
--c-btn-shadow2: #f5f9fd;
--c-txt1: #aaa;
--c-txt2: #111;
}
.dark{
background-color: #333;
--c-wrap-shadow1: #292929;
--c-wrap-shadow2: #202020;
--c-wrap-bg: #505050;
--c-btn-shadow1: #323232;
--c-btn-shadow2: #444;
--c-txt1: #888;
--c-txt2: #fff;
}
纯代码版本
<div id="btnWrapper">
<div class="btn active">开灯</div>
<div class="btn">关灯</div>
</div>
let wrapper = document.getElementById('btnWrapper');
wrapper.style.setProperty('--groove-left', '12px');
let btns = document.getElementsByClassName('btn');
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function (e) {
ThemeChange(i === 1);
resetBtn(btns);
wrapper.style.setProperty('--groove-left', `calc(12px + ${i * 50}%)`);
wrapper.style.setProperty('--wraper-origin', `${i === 0 ? '75% top' : '25% top'}`);
wrapper.style.setProperty('--wraper-rotate', `${i === 0 ? -8 : 8}deg`);
wrapper.className = 'rotateWrap';
setTimeout(() => {
btns[i].className = 'btn active';
}, 500);
setTimeout(() => {
wrapper.className = ''
}, 550);
})
}
// 重置按钮类名
function resetBtn(btns) {
for (let i = 0; i < btns.length; i++) {
setTimeout(() => {
btns[i].className = 'btn';
}, 100)
}
}
// 改变主题
function ThemeChange(bol) {
let body = document.body;
body.className = bol ? 'dark' : ''
}
*{
margin:0;
padding:0;
box-sizing:border-box;
}
body {
background-color: #edf1f4;
perspective: 500px;
--c-wrap-shadow1: #f5f9fd;
--c-wrap-shadow2: #d8dbe5;
--c-wrap-bg: #e2e6eb;
--c-btn-shadow1: #d9dbe6;
--c-btn-shadow2: #f5f9fd;
--c-txt1: #aaa;
--c-txt2: #111;
transition: background-color 0.4s linear;
}
.dark {
background-color: #333;
--c-wrap-shadow1: #292929;
--c-wrap-shadow2: #202020;
--c-wrap-bg: #505050;
--c-btn-shadow1: #323232;
--c-btn-shadow2: #444;
--c-txt1: #888;
--c-txt2: #fff;
}
#btnWrapper {
position: relative;
width: 380px;
height: 80px;
padding: 12px 16px;
margin: 300px auto 0;
border-radius: 12px;
overflow: hidden;
background-color: var(--c-wrap-bg);
box-shadow: -10px -10px 15px var(--c-wrap-shadow1), 10px 10px 15px var(--c-wrap-shadow2);
transform-origin: var(--wraper-origin);
transition: transform 0.4s cubic-bezier(0, 0, 0.48, 1), box-shadow 0.4s linear, background-color 0.4s linear;
}
.rotateWrap {
transform: rotateY(var(--wraper-rotate));
}
#btnWrapper::before {
content: "";
position: absolute;
left: var(--groove-left);
top: 12px;
width: calc(50% - 16px - 8px);
height: calc(100% - 24px);
border-radius: 12px;
box-shadow: inset 8px 8px 6px var(--c-btn-shadow1), inset -5px -5px 15px var(--c-btn-shadow2), inset -5px -5px 15px var(--c-btn-shadow2), inset 7px 7px 6px var(--c-btn-shadow1);
transition: left 1s cubic-bezier(0.82, 0.12, 0.18, 0.88), box-shadow 0.4s linear;
}
.btn {
float: left;
display: flex;
align-items: center;
justify-content: center;
width: 50%;
height: 100%;
padding: inherit;
color: var(--c-txt1);
transition: color 0.4s linear;
animation: txtOutScale 0.6s linear;
cursor: pointer;
}
.active {
color: var(--c-txt2);
transform: scale(1.1);
animation: txtEnterScale 0.4s linear;
}
@keyframes txtEnterScale {
0% {
transform: scale(1);
}
80% {
transform: scale(1.15);
}
100% {
transform: scale(1.1);
}
}
@keyframes txtOutScale {
0% {
transform: scale(1.1);
}
80% {
transform: scale(0.95);
}
100% {
transform: scale(1);
}
}
getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性
兔子动效
<!--
* @Author: yangyuguang
* @Date: 2022-12-29 16:46:51
* @LastEditors: yangyuguang
* @LastEditTime: 2023-01-09 16:49:53
* @FilePath: /my_test/src/components/text.vue
-->
<template>
<div class="container">
<div class="loading">
<div class="rabbit"></div>
<div class="clouds"></div>
<div class="text">Loading...</div>
</div>
</div>
</template>
<script>
export default {
methods: {},
mounted () {}
}
</script>
<style scoped>
.container {
display: grid;
height: 100vh;
place-content: center;
}
.loading {
width: 320px;
height: 180px;
background: #e2b29f;
border-radius: 10px;
display: grid;
place-content: center;
position: relative;
}
.rabbit {
width: 5em;
height: 3em;
background-color: #fff;
border-radius: 70% 90% 60% 50%;
position: relative;
/* x偏移量 | y偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色 */
box-shadow: -0.2em 1em 0 -0.75em #b78e81;
transform: translate(-2em, 0);
animation: hop 1s infinite linear;
z-index: 1;
}
/* 利用宽高画兔子的尾巴 利用box-shadow画兔子的眼睛和腿 */
.rabbit:before {
content: '';
position: absolute;
width: 1em;
height: 1em;
background: white;
border-radius: 100%;
top: 0.5em;
left: -0.3em;
box-shadow: 4em 0.4em 0 -0.35em #3f3334, 0.5em 1em 0 white,
4em 1em 0 -0.3em white, 4em 1em 0 -0.3em white, 4em 1em 0 -0.4em white;
animation: kick 1s infinite linear;
}
/* 利用矩形和box-shdow画兔子的一只耳朵和另一只耳朵 */
.rabbit:after {
content: '';
position: absolute;
width: 0.75em;
height: 2em;
background: #fff;
border-radius: 50% 100% 0 0;
transform: rotate(-30deg);
right: 1em;
top: -1em;
border-top: 1px solid #f7f5f4;
border-left: 1px solid #f7f5f4;
box-shadow: -0.5em 0 0 -0.1em white;
}
/* 宽高创建第一个拱形 box-shadow另外新加两个 */
.clouds {
background: white;
width: 2em;
height: 2em;
border-radius: 100% 100% 0 0;
position: relative;
top: -4em;
opacity: 0;
transform: translate(0, 0);
animation: cloudy 1s infinite linear forwards;
box-shadow: 5em 2em 0 -0.3em white, -2em 2em 0 0 white;
}
.clouds:before,
.clouds:after {
content: '';
position: absolute;
box-shadow: 5em 2em 0 -0.3em white, -2em 2em 0 white;
}
.clouds:before {
width: 1.25em;
height: 1.25em;
border-radius: 100% 100% 0 100%;
background: #fff;
left: -30%;
bottom: 0;
}
.clouds:after {
width: 1.25em;
height: 1.25em;
border-radius: 100% 100% 100% 0;
background: #fff;
left: 65%;
bottom: 0;
}
.text {
position: absolute;
left: 0;
right: 0;
bottom: 0.5em;
display: grid;
place-content: center;
color: white;
font-size: 2em;
/* 定义无小写字母,仅有大写字母 */
text-transform: uppercase;
letter-spacing: 0.2em;
font-family: fantasy;
}
@keyframes cloudy {
40% {
opacity: 0.75;
transform: translate(-3em, 0);
}
50% {
opacity: 0;
transform: translate(-4em, 0);
}
90% {
transform: translate(0, 0);
}
}
@keyframes hop {
20% {
transform: rotate(-10deg) translate(1em, -2em);
box-shadow: -2em 3em 0 -1em #b78e81;
}
40% {
transform: rotate(10deg) translate(3em, -4em);
box-shadow: -2em 3.25em 0 -1.1em #b78e81;
}
60%,
75% {
transform: rotate(0) translate(4em, 0);
box-shadow: -0.2em 1em 0 -0.75em #b78e81;
}
}
@keyframes kick {
20%,
50% {
box-shadow: 4em 0.4em 0 -0.35em #3f3334, 0.5em 1.5em 0 white,
4em 1.75em 0 -0.3em white, 4em 1.75em 0 -0.3em white,
4em 1.9em 0 -0.4em white;
}
40% {
box-shadow: 4em 0.4em 0 -0.35em #3f3334, 0.5em 2em 0 white,
4em 1.75em 0 -0.3em white, 4.2em 1.75em 0 -0.2em white,
4.4em 1.9em 0 -0.2em white;
}
}
</style>