产品360度展示

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>产品360度展示</title>
  <link rel="stylesheet" href="./css/style.css">
</head>

<body>
  <h1>产品展示</h1>
  <p class="tip">点击屏幕,360度产品展示</p>
  <div class="computer">
    <div class="inner">
      <div class="screen">
        <div class="face-one">
          <div class="camera"></div>
          <div class="display">
            <div class="shade">
              <div class="computer-screen">
                <div class="loading-bar"></div>
                <div class="loading-text">正在启动...</div>
              </div>
            </div>
          </div>
          <span>蓝桥云课</span>
        </div>
        <title>Layer 1</title>
      </div>
      <div class="computerbody">
        <div class="face-one">
          <div class="touchpad">
          </div>
          <div class="keyboard">
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key space"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
            <div class="key f"></div>
          </div>
        </div>
        <div class="pad one"></div>
        <div class="pad two"></div>
        <div class="pad three"></div>
        <div class="pad four"></div>
      </div>
    </div>
    <div class="shadow"></div>
  </div>
  <script src="./js/utils.js"></script>
  <script src="./js/index.js"></script>
</body>

</html>
// 获取页面中类名为'computer-screen'的元素,代表电脑屏幕显示区域内的元素,
// 后续用于监听动画结束事件并进行相关操作
const computerScreen = document.querySelector('.computer-screen');

// 为computerScreen元素添加'animationend'事件监听器,当该元素上绑定的动画结束时,会执行回调函数
// 此回调函数的目的是在动画结束后,对电脑屏幕显示区域元素进行一些清理和样式调整操作
computerScreen.addEventListener('animationend', () => {
    // 给computerScreen元素添加'hide-progress'类名,
    // 推测在CSS样式表中定义了相应的样式来隐藏进度条相关元素,比如将进度条的显示属性设为none等
    computerScreen.classList.add('hide-progress');
    // 直接移除computerScreen元素,可能是在电脑启动动画完成后,不再需要显示启动进度条相关内容了,所以进行清理
    computerScreen.remove();
});

// 获取页面中类名为'inner'的元素,代表电脑主体部分,后续作为动画操作的主要对象之一
const macbook = document.querySelector('.inner');
// 获取页面中类名为'screen'的元素,代表电脑屏幕部分,在动画函数中会对其进行各种变换操作来实现动画效果
const screen = document.querySelector('.screen');
// 获取页面中类名为'shadow'的元素,用于模拟电脑的影子效果,在动画过程中通过改变其样式属性来呈现不同的投影外观
const shadow = document.querySelector('.shadow');

// 动画函数1 - 屏幕后折 + 向左微微旋转
// 该函数接受一个元素参数element,在具体调用时传入电脑主体元素(如macbook),对其及相关屏幕、影子元素设置样式变换,实现特定动画效果
const ani1 = (element) => {
    // 定义动画持续时间为0.4秒,这个时间决定了该阶段动画从开始到结束所花费的时长
    const aniTime = 0.4;
    return new Promise((resolve) => {
        // 同时设置element(电脑主体元素)、screen(电脑屏幕元素)、shadow(电脑影子元素)的transition属性,
        // 表示它们的CSS变换将以指定的时间(aniTime)和缓动函数(ease)进行过渡,实现平滑的动画效果
        element.style.transition =
            screen.style.transition =
            shadow.style.transition =
                `transform ${aniTime}s ease`;
        // 设置电脑屏幕的绕X轴旋转角度为50度,实现屏幕后折的视觉效果
        screen.style.transform = 'rotateX(50deg)';
        // 对传入的element(电脑主体元素)设置多个轴向的旋转角度,使其向左微微旋转等,呈现出特定姿态
        element.style.transform = 'rotateX(-20deg) rotateY(-20deg) rotateZ(0deg)';
        // 设置电脑主体元素的背景位置为'left bottom',可能用于配合动画呈现出更好的视觉效果,比如改变背景显示区域等
        element.style.backgroundPosition = 'left bottom';
        // 设置影子元素的绕X、Y、Z轴的旋转角度,改变影子的外观和位置,使其与电脑主体动画相匹配
        shadow.style.transform = 'rotateX(80deg) rotateY(10deg) rotateZ(0deg)';
        // 设置影子元素的boxShadow属性,调整阴影的大小、颜色、模糊度等,增强动画的立体感和真实感
        shadow.style.boxShadow = '0 0 60px 40px rgba(0, 0, 0, 0.3)';
        // 通过setTimeout设置一个定时器,在动画持续时间(aniTime * 1000毫秒)结束后,调用resolve函数并传入element,
        // 这样可以让该动画函数以Promise的形式返回,方便后续在异步流程中进行链式调用,确保动画按顺序执行
        setTimeout(() => resolve(element), aniTime * 1000);
    });
};

// 动画函数2 - 向后旋转展示电脑底部,并合上电脑屏幕
// 功能和结构与ani1类似,用于实现不同阶段的动画效果,通过改变元素的样式属性来展示电脑不同角度的外观
const ani2 = (element) => {
    const aniTime = 0.8;
    return new Promise((resolve) => {
        element.style.transition =
            screen.style.transition =
            shadow.style.transition =
                `transform ${aniTime}s ease`;

        screen.style.transform = 'rotateX(-90deg)';
        element.style.transform = 'rotateX(50deg) rotateY(200deg) rotateZ(0deg)';
        element.style.backgroundPosition = '-150px top';
        shadow.style.transform = 'rotateX(30deg) rotateY(25deg) rotateZ(-20deg)';
        shadow.style.boxShadow = '0 0 50px 30px rgba(0, 0, 0, 0.2)';
        setTimeout(() => resolve(element), aniTime * 1000);
    });
};

// 动画函数3 - 电脑向上仰起,展示电脑上盖
const ani3 = (element) => {
    const aniTime = 0.8;
    return new Promise((resolve) => {
        element.style.transition =
            screen.style.transition =
            shadow.style.transition =
                `transform ${aniTime}s ease`;

        screen.style.transform = 'rotateX(15deg)';
        element.style.transform = 'rotateX(-60deg) rotateY(150deg) rotateZ(0deg)';
        element.style.backgroundPosition = 'left bottom';
        shadow.style.transform = 'rotateX(80deg) rotateY(0deg) rotateZ(50deg)';
        shadow.style.boxShadow = '0 0 35px 15px rgba(0, 0, 0, 0.1)';
        setTimeout(() => resolve(element), aniTime * 1000);
    });
};

// 动画函数4 - 电脑向下缓慢放平,展示电脑侧面视角
const ani4 = (element) => {
    const aniTime = 1.2;
    return new Promise((resolve) => {
        element.style.transition =
            screen.style.transition =
            shadow.style.transition =
                `transform ${aniTime}s ease-out`;

        screen.style.transform = 'rotateX(-40deg)';
        element.style.transform = 'rotateX(-20deg) rotateY(130deg) rotateZ(0deg)';
        element.style.backgroundPosition = 'right top';
        shadow.style.transform = 'rotateX(80deg) rotateY(0deg) rotateZ(-50deg) translateX(30px)';
        shadow.style.boxShadow = '0 0 35px 15px rgba(0, 0, 0, 0.3)';
        setTimeout(() => resolve(element), aniTime * 1000);
    });
};

// 动画函数5 - 电脑旋转回正面
const ani5 = (element) => {
    const aniTime = 0.9;
    return new Promise((resolve) => {
        element.style.transition =
            screen.style.transition =
            shadow.style.transition =
                `transform ${aniTime}s ease`;

        screen.style.transform = 'rotateX(0deg)';
        element.style.transform = 'rotateX(-20deg) rotateY(360deg) rotateZ(0deg)';
        element.style.backgroundPosition = 'right center';
        shadow.style.transform = 'rotateX(80deg) rotateY(0deg) rotateZ(0deg)';
        shadow.style.boxShadow = '0 0 60px 40px rgba(0, 0, 0, 0.3)';
        setTimeout(() => resolve(element), aniTime * 1000);
    });
};

// 定义一个布尔变量canAnimation,用于控制动画是否可以执行,初始值为true,表示可以开始动画
let canAnimation = true;
// 给整个文档添加一个'click'事件监听器,当用户点击页面时,会触发animationQuence函数
document.addEventListener('click', animationQuence);

// 定义一个异步函数animationQuence,用于处理整个动画序列的执行逻辑,包括判断是否可执行动画、执行动画序列以及动画结束后的状态重置等操作
async function animationQuence() {
    // 如果canAnimation为false,说明当前动画正在执行或者还未准备好执行下一次动画,直接返回,避免重复触发动画
    if (!canAnimation) {
        return;
    }
    // 将canAnimation设为false,标记当前动画开始执行,防止在动画执行过程中再次触发点击事件导致动画重复执行
    canAnimation = false;
    // 调用pipeline函数,并传入电脑主体元素macbook和包含所有动画函数(ani1 - ani5)的数组,
    // 通过await等待整个动画序列执行完成,确保各个动画按顺序依次展示
    await pipeline(macbook, [ani1, ani2, ani3, ani4, ani5]);

    // 动画序列结束后,重置电脑主体、屏幕等相关元素的transition(过渡)属性为默认值('unset'),
    // 取消之前设置的动画过渡效果,避免对后续操作产生影响
    macbook.style.transition = 'unset';
    screen.style.transition = 'unset';
    // 重置电脑屏幕的transform属性,将其旋转角度恢复到初始的0度,使其回到正面显示状态
    screen.style.transform = 'rotateX(0deg)';
    // 重置电脑主体元素的transform属性,恢复到初始的旋转角度状态,呈现出初始的外观姿态
    macbook.style.transform = 'rotateX(-20deg) rotateY(0deg) rotateZ(0deg)';
    // 重置电脑主体元素的背景位置为初始的'right center',恢复到默认的背景显示效果
    macbook.style.backgroundPosition = 'right center';
    // 在控制台输出提示信息,表示所有动画步骤都已完成
    console.log('All steps completed!');
    // 将canAnimation重新设为true,以便下次点击页面时可以再次触发动画序列执行
    canAnimation = true;
}

// pipeline函数用于按照顺序依次执行给定序列中的函数,并将初始值传递给每个函数进行处理。
// 在这里的应用场景下,主要是用于依次执行一系列动画函数,实现连贯的动画序列效果。
// 
// @param {*} initialValue 初始值,在具体的使用场景中可能是要进行动画操作的DOM元素(如电脑主体元素等),
// 会被传递给序列中的每个函数作为操作对象。
// @param {Array} sequence 由普通函数或Promise函数组成的数组,例如动画相关的函数数组(像ani1、ani2等动画函数构成的数组),
// 数组中的函数将按照顺序依次被执行。
// @return {Promise} 返回一个Promise对象,该Promise对象在整个函数序列执行完成后被resolve,
// 可以通过await等方式来等待整个序列执行完毕。
const pipeline = (initialValue, sequence) => {
    return sequence.reduce((prevPromise, func) => {
        return prevPromise.then(() => func(initialValue));
    }, Promise.resolve());
};

// 检测需要,请勿删除
try {
    module.exports = { pipeline };
} catch { }
/**
 * pipeline函数用于按照顺序依次执行给定的函数序列,并将初始值传递给每个函数进行处理,最终返回处理后的结果。
 * 整个函数执行过程是异步的,返回一个Promise对象,以便外部可以通过await等方式来处理执行结果。
 *
 * @param {*} initialValue 初始值,它可以是任意类型的数据,在具体应用场景中可能是一个特定的对象(比如DOM元素等),
 * 会作为参数依次传递给函数序列中的每个函数进行操作。
 * @param {Array} sequence 由普通函数或Promise函数组成的数组,代表需要按顺序执行的一系列函数,
 * 每个函数接收initialValue作为参数,并对其进行相应的处理,然后返回处理后的结果(返回值类型根据具体函数逻辑而定)。
 * @return {Promise} 返回一个Promise对象,该Promise在函数序列中的所有函数都执行完成后被resolve,
 * 其resolve的值为最后一个函数执行完后返回的经过一系列处理后的initialValue。
 */
const pipeline = async (initialValue, sequence) => {
    // 使用for...of循环遍历sequence数组,按顺序依次执行数组中的每个函数
    for (let fn of sequence) {
        // 调用当前函数fn,并将initialValue作为参数传入,然后打印该函数的返回值。
        // 这样可以方便查看每个函数执行后的结果,有助于调试和了解函数执行情况,但在实际生产环境中,
        // 如果不需要查看中间结果,可根据情况选择是否保留此打印语句,因为过多的打印可能影响性能或干扰日志输出。
        console.log(fn(initialValue));
        // 将当前函数fn执行后的返回值重新赋值给initialValue,用于下一个函数的执行。
        // 这里使用await关键字等待Promise.resolve(fn(initialValue))执行完成,确保当前函数执行完毕后再进行下一步操作。
        // 注意,此处的Promise.resolve其实是多余的,因为如果fn本身返回的是一个Promise对象,Promise.resolve不会改变其状态,
        // 如果fn返回的是普通值,Promise.resolve会将其包装成一个已解决的Promise对象,所以可以直接使用await fn(initialValue),
        // 但这样写也不影响功能,只是多了一层不必要的包装操作。
        initialValue = await Promise.resolve(fn(initialValue));
    }
    // 在所有函数都按顺序执行完成后,返回最终经过一系列处理后的initialValue,
    // 外部调用者可以通过await pipeline(...)获取这个最终的返回值,以进行后续的操作。
    return initialValue;
};

// 检测需要,请勿删除
try {
    module.exports = { pipeline };
} catch { }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值