当前位置: 首页 > article >正文

前端图像处理(二)

目录

一、上传

1.1、文件夹上传以及进度追踪

1.2、拖拽上传

1.3、图片裁剪上传原理

二、图片布局

2.1、渐进式图片

2.2、图片九宫格

2.3、轮播图(Js)

2.3.1、3D动画轮播图

2.3.2、旋转切换的轮播图

2.4、卡片移入+翻转效果

2.5、环绕式照片墙

一、上传

1.1、文件夹上传以及进度追踪

效果展示:

交互:如何选择多个文件?如何选择文件夹?如何拖拽文件和文件夹?

网络:如何实现多文件上传?如何实现进度追踪?如何实现取消上传?

  • 上传逻辑uploadFile 函数创建一个 XMLHttpRequest 对象,设置请求方法和 URL,并打开请求。它使用 FormData 来包含要上传的文件。
  • 进度追踪:通过监听 xhr.upload.onprogress 事件,可以追踪上传进度,上传进度的计算依赖于 event.lengthComputable 属性
  • 取消上传uploadFile 函数返回一个取消函数,当调用这个函数时,它会调用 xhr.abort() 来取消上传。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .container {
        border: 1px solid black;
        width: 50%;
        height: 300px;
      }
    </style>
  </head>
  <body>
    <div class="container"></div>
    <script>
      const div = document.querySelector(".container");
      div.ondragenter = (e) => {
        e.preventDefault();
      };
      div.ondragover = (e) => {
        e.preventDefault();
      };
      div.ondrop = (e) => {
        e.preventDefault();
        const items = e.dataTransfer.items;
        for (const item of items) {
          // item 是 DataTransferItem 对象
          const entry = item.webkitGetAsEntry(); // 拿到 FileEntry 对象
          processEntry(entry);
        }
      };
      // 使用递归处理拖放的文件和文件夹
      function processEntry(entry) {
        if (entry.isFile) {
          // 处理文件:拿到 File 文件
          entry.file((file) => {
            uploadFile(file);
          });
        } else if (entry.isDirectory) {
          // 处理文件夹:文件夹中的文件 File 文件对象
          const reader = entry.createReader();
          reader.readEntries((entries) => {
            entries.forEach((entry) => {
              processEntry(entry); // 递归处理每个条目
            });
          });
        }
      }
      function uploadFile(file) {
        const formData = new FormData();
        formData.append("file", file);
        const xhr = new XMLHttpRequest();
        xhr.open("POST", "https://jsonplaceholder.typicode.com/posts/", true);
        // 上传进度追踪
        xhr.upload.onprogress = (event) => {
          if (event.lengthComputable) {
            const percentComplete = (event.loaded / event.total) * 100;
            console.log(`Upload progress:${percentComplete}%`); //fetch无法追踪请求进度
          }
        };
        // 上传完成
        xhr.onload = () => {
          if (xhr.status === 200) {
            console.log("Upload completed");
          } else {
            console.log("Upload error");
          }
        };
        // 上传错误
        xhr.onerror = () => {
          console.log("Upload error");
        };
        // 发送请求
        xhr.send(formData);
        // 返回取消函数
        return () => {
          xhr.abort(); //取消上传
        };
      }
    </script>
  </body>
</html>

1.2、拖拽上传

画一个盒子,盒子里放个上传的input,主要就是看在外界图片直接拖拽进盒子时,要如何处理。

      doms.ondragenter = (e) => {
        e.preventDefault();
        e.target.classList.add("draging");
      };
      doms.ondragover = (e) => {
        e.preventDefault();
      };
      doms.ondrop = (e) => {
        e.preventDefault();
        e.target.classList.remove("draging");
        const files=e.dataTransfer.files
        const types=e.dataTransfer.types
        console.log("FileList数据",files,"文件类型",types)
      };

1.3、图片裁剪上传原理

  <body>
    <input type="file" />
    <img class="preview" />
    <button>上传裁剪后的结果</button>
    <script>
      const inp = document.querySelector("input");
      const img = document.querySelector("img");
      inp.onchange = (e) => {
        const file = e.target.files[0];
        const reader = new FileReader();
        reader.onload = (e) => {
          img.src = e.target.result;
        };
        reader.readAsDataURL(file);
      };
    //   假设已经拿到剪裁后的结果
      function uploadResult({ cutWidth, cutHeight, cutX, cutY }) {
        const cvs = document.createElement("canvas");
        const ctx = cvs.getContext("2d");
        cvs.width = 200;
        cvs.height = 200;
        ctx.drawImage(
          img,
          cutX,
          cutY,
          cutWidth,
          cutHeight,
          0,
          0,
          cvs.width,
          cvs.height
        );
        document.body.appendChild(cvs);
      }
      const btn = document.querySelector("button");
      btn.onclick = () => {
        uploadResult({
          cutWidth: 200,
          cutHeight: 200,
          cutX: 100,
          cutY: 100,
        });
      };
    </script>
  </body>

二、图片布局

2.1、渐进式图片

【场景】加载大图:一开始是模糊的图片,慢慢变的清晰

UI可以给出一张jpg【支持多帧】:第一帧模糊小图,第二帧高清大图,给不了就自己写:

这个组件在使用时直接传入图片即可!

<template>
  <div class="progressive">
    <img :src="placeholder" class="img placeholder" />
    <img :src="origin" class="img origin" @load="handleLoaded" />
  </div>
</template>
<script>
export default {
  // placeholder:模糊小图   origin:原始大图
  props: ['placeholder', 'origin'],
  methods: {
    handleLoaded(e) {
      e.target.parentElement.classList.add('loaded')
    }
  }
}
</script>
<style scoped>
.progressive {
  position: relative;
}
.img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  transition: 0.6s;
}
.origin {
  opacity: 0;
  position: absolute;
  left: 0;
  top: 0;
  /* 给 .origin 图像应用一个模糊效果 */
  filter: blur(10px);
}
.loaded .origin {
  opacity: 1;
  /* 移除模糊效果,使图像变得清晰 */
  filter: blur(0);
}
</style>

2.2、图片九宫格

    <style>
      /* 每个图片都是500 * 500 的 */
      .img-box {
        width: 500px;
        border: 1px solid red;
      }
      .img-container {
        height: 500px;
        display: grid;
        grid-template-columns: repeat(3, 1fr);
      }
      .img-item {
        box-shadow: inset 0 0 0 5px #fff;
        transition: 0.5s;
        background-size: 500px 500px;
        background-image: url(./001.png);
        background-position: var(--bgx, 0) var(--bgY, 0);
        transform: translate(var(--disX, 0), var(--disY, 0));
      }
      .img-container:hover .img-item {
        box-shadow: inset 0 0 0 0 #fff;
      }
    </style>
  <body>
    <div class="img-box">
      <div class="img-container">
        <div class="img-item"></div>
        <div class="img-item"></div>
        <div class="img-item"></div>
        <div class="img-item"></div>
        <div class="img-item"></div>
        <div class="img-item"></div>
        <div class="img-item"></div>
        <div class="img-item"></div>
        <div class="img-item"></div>
      </div>
    </div>
    <script>
      const items = document.querySelectorAll(".img-item");
      for (let i = 0; i < items.length; i++) {
        const r = Math.floor(i / 3); // 行索引
        const c = i % 3; // 列索引
        const bgX = -c * (500 / 3) + "px"; // 背景X位置
        const bgY = -r * (500 / 3) + "px"; // 背景Y位置
        // 位移限制在容器内
        const disX = c; // 水平位移
        const disY = r; // 垂直位移
        items[i].style.setProperty("--bgx", bgX);
        items[i].style.setProperty("--bgY", bgY);
        items[i].style.setProperty("--disX", disX + "px");
        items[i].style.setProperty("--disY", disY + "px");
      }
    </script>
  </body>

2.3、轮播图(Js)

2.3.1、3D动画轮播图

   <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      .carousel-box {
        width: 65%;
        height: 80vh;
        background-color: black;
        position: relative;
        perspective: 1000px; /* 添加透视效果 */
        transform-style: preserve-3d; /* 保持3D变换 */
      }
      .carousel-box span {
        font-size: 40px;
        color: aqua;
        cursor: pointer;
      }
      .carousel-item {
        position: absolute;
        left: 30%;
        top: 30%;
        transform: translate(-50%, -50%) translateZ(0); /* 使用translateZ添加3D效果 */
        transition: transform 0.1s;
      }
      .prev-btn {
        position: absolute;
        left: 10px;
        top: 50%;
        transform: translateY(-50%);
        z-index: 99;
      }
      .next-btn {
        position: absolute;
        right: 10px;
        top: 50%;
        transform: translateY(-50%);
        z-index: 99;
      }
    </style>
  <body>
    <div class="carousel-box">
      <span class="prev-btn">《</span>
      <span class="next-btn">》</span>
      <img src="https://picsum.photos/id/41/400/300/" class="carousel-item" />
      <img src="https://picsum.photos/id/42/400/300/" class="carousel-item" />
      <img src="https://picsum.photos/id/43/400/300/" class="carousel-item" />
      <img src="https://picsum.photos/id/44/400/300/" class="carousel-item" />
      <img src="https://picsum.photos/id/45/400/300/" class="carousel-item" />
      <img src="https://picsum.photos/id/46/400/300/" class="carousel-item" />
      <img src="https://picsum.photos/id/47/400/300/" class="carousel-item" />
      <img src="https://picsum.photos/id/48/400/300/" class="carousel-item" />
      <img src="https://picsum.photos/id/49/400/300/" class="carousel-item" />
      <img src="https://picsum.photos/id/50/400/300/" class="carousel-item" />
    </div>
    <script>
      const items = document.querySelectorAll(".carousel-item");
      let index = 3; //当前显示的图片索引
      function layout() {
        const offsetStep = 100; //每两张图片之间的偏移量
        const scaleStep = 0.5; //每两张图片之间的缩放比例差
        const opacityStep = 0.5; //每两张图片之间的透明度差
        for (let i = 0; i < items.length; i++) {
          const item = items[i];
          const dis = Math.abs(i - index);
          const sign = Math.sign(i - index);
          let xOffset = (i - index) * offsetStep;
          if (i != index) {
            xOffset = xOffset + 100 * sign;
          }
          const scale = scaleStep ** dis;
          const rotateY = 45 * -sign; // 增加旋转角度
          item.style.transform = `translateX(${xOffset}px) scale(${scale}) rotateY(${rotateY}deg)`;
          const opacity = opacityStep ** dis;
          item.style.opacity = opacity;
          const zIndex = items.length - dis; //层级
          item.style.zIndex = zIndex;
        }
      }
      layout();
      const prev = document.querySelector(".prev-btn");
      const next = document.querySelector(".next-btn");
      prev.addEventListener("click", function () {
        index--;
        if (index < 0) {
          index = items.length - 1;
        }
        layout();
      });
      next.addEventListener("click", function () {
        index++;
        if (index > items.length - 1) {
          index = 0;
        }
        layout();
      });
      items.forEach((item, i) => {
        item.addEventListener("click", function (event) {
          event.preventDefault(); // 阻止默认行为,即不触发下载
          index = i;
          layout();
        });
      });
    </script>
  </body>

2.3.2、旋转切换的轮播图

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="styles.css" />
</head>
<body>
  <div class="container">
    <div class="inner">
      <img src="https://picsum.photos/id/51/600" alt="">
      <img src="https://picsum.photos/id/52/600" alt="">
      <img src="https://picsum.photos/id/53/600" alt="">
      <img src="https://picsum.photos/id/54/600" alt="">
      <img src="https://picsum.photos/id/55/600" alt="">
      <img src="https://picsum.photos/id/56/600" alt="">
    </div>
  </div>
</body>
</html>

styles.scss文件:

@use "sass:math";
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  background-color: black;
}
$size: 200px;
$n: 6;
$pDeg: 360deg / $n;
$r: $size/2;
$R: $r/math.sin($pDeg/2);
$innerSize: $R * 2;
.container {
  width: $size;
  height: $size;
  outline: 5px solid #fff;
  border-radius: 50%;
  margin: 50px auto;
  display: flex;
  justify-content: center;
  overflow: hidden;
}
.inner {
  width: $innerSize;
  height: $innerSize;
  border-radius: 50%;
  // background-color: #f40;//基于红色圈在转:父元素去掉overflow: hidden可以查看原理
  flex-shrink: 0;
  margin-top: $r;
  position: relative;
  img {
    width: $size;
    height: $size;
    border-radius: 50%;
    position: absolute;
    left: 50%;
    margin-left: -$size/2;
    top: -$r;
    transform-origin: center #{$r + $R};
    @for $i from 1 through $n {
      &:nth-child(#{$i}) {
        transform: rotate($pDeg * ($i - 1));
      }
    }
  }
}
$u: 1 / $n * 100%;
$stopPercent: 0.6 * $u;
@keyframes moving {
  @for $i from 1 through $n {
    $p: $u * $i;
    $deg: $pDeg * $i;
    #{$p - $stopPercent},
    #{$p} {
      transform: rotate(-$deg);
    }
  }
}
.inner {
  animation: moving 10s infinite;
}

2.4、卡片移入+翻转效果

移入:从平面变成3D翻起来效果

 <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      body {
        width: 100vw;
        height: 100vh;
        margin: 0;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      .card {
        width: 428px;
        height: 475px;
        margin: 0 50px;
        transform: translateY(-50%);
        position: relative;
      }
      .card img {
        width: 100%;
        position: absolute;
        transition: 0.5s;
      }
      .card .cover {
        z-index: 1;
      }
      .card:hover .cover {
        box-shadow: 0 35px 35px -8px rgb(0, 0, 0, 0.75);
        transform: perspective(500px) rotateX(25deg);
      }
      .card .title {
        z-index: 3;
        bottom: 0;
        right: 0;
      }
      .card:hover .title {
        transform: perspective(500px) translate3d(0, -25px, 50px);
      }
      .card .hero {
        z-index: 2;
        opacity: 0;
      }
      .card:hover .hero {
        opacity: 1;
        transform: perspective(500px) translate3d(0, -50px, 50px);
      }
    </style>
  <body>
    <div class="card">
      <img src="./images/ping.png" alt="" class="cover" />
      <img src="./images/wenzi.png" alt="" class="title" />
      <img src="./images/liqilai.png" alt="" class="hero" />
    </div>
  </body>

翻转:一张卡片展示正面,翻转后看到另一面

  <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      .card {
        perspective: 500px; /* 给父元素设置透视效果 */
        transform-style: preserve-3d; /* 保证子元素的 3D 变换效果生效 */
        width: 200px; /* 设置宽度和高度,确保翻转效果可以看到 */
        height: 300px;
        position: relative; /* 确保子元素的定位 */
      }
      .card .face,
      .card .back {
        position: absolute; /* 确保两张图片在同一位置 */
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        backface-visibility: hidden; /* 隐藏背面的图片 */
        transition: transform 0.6s; /* 加入过渡效果 */
      }
      .card .face {
        transform: rotateY(0deg); /* 正面显示 */
      }
      .card .back {
        transform: rotateY(180deg); /* 背面显示 */
      }
      .card:hover .face {
        transform: rotateY(-180deg); /* 当悬停时,翻转正面 */
      }
      .card:hover .back {
        transform: rotateY(0deg); /* 背面正向显示 */
      }
    </style>
  <body>
    <div class="card">
      <img src="./images/zheng.png" alt="" class="face" />
      <img src="./images/bei.jpg" alt="" class="back" />
    </div>
  </body>

2.5、环绕式照片墙

使用sass

下载:npm install -g sass

编译:sass styles.scss styles.css

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div class="ring">
      <img src="https://picsum.photos/id/41/400/600/" alt="" />
      <img src="https://picsum.photos/id/42/400/600/" alt="" />
      <img src="https://picsum.photos/id/43/400/600/" alt="" />
      <img src="https://picsum.photos/id/44/400/600/" alt="" />
      <img src="https://picsum.photos/id/45/400/600/" alt="" />
      <img src="https://picsum.photos/id/46/400/600/" alt="" />
      <img src="https://picsum.photos/id/47/400/600/" alt="" />
      <img src="https://picsum.photos/id/48/400/600/" alt="" />
      <img src="https://picsum.photos/id/49/400/600/" alt="" />
      <img src="https://picsum.photos/id/50/400/600/" alt="" />
    </div>
    <script>
      const ring = document.querySelector(".ring");
      let angle = 0;
      // 创建定时器变量
      let intervalId = null;
      function rotateRing() {
        intervalId = setInterval(() => {
          // 每次增加10度
          angle += 10;
          // 如果角度达到或超过360度,重置为0
          if (angle >= 360) {
            angle = 0;
          }
          ring.style.transform = `rotateY(${angle}deg)`;
        }, 100); // 每100毫秒执行一次
      }
      // 开始旋转
      rotateRing();
      // 监听鼠标点击事件,停止旋转
      document.addEventListener("click", () => {
        clearInterval(intervalId); // 停止定时器
      });
    </script>
  </body>
</html>
// ======================styles.scss文件===================
@use "sass:math";
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html {
  overflow: hidden;
}
body {
  background-color: black;
  perspective: 2000px;
}
$imgWidth: 300px;
$imgHeight: 400px;
$n: 10; // 总的图片数量
$pDeg: 360deg / $n; // 每个图片的角度间隔
$r: 500px; // 半径
.ring {
  width: 100vw;
  height: 100vh;
  position: relative;
  transform-style: preserve-3d;
  img {
    position: absolute;
    width: $imgWidth;
    height: $imgHeight;
    left: 50%;
    top: 50%;
    margin-left: calc(-1 * #{$imgWidth} / 2);
    margin-top: calc(-1 * #{$imgHeight} / 2);
    backface-visibility: hidden;
    opacity: 0.5;
    transition: .5s;
    &:hover {
      opacity: 1;
    }
    // 使用循环将图片分布到圆形轨迹上
    @for $i from 1 through $n {
      &:nth-child(#{$i}) {
        $deg: $pDeg * ($i - 1); // 当前图片的角度
        $x: math.sin($deg) * $r; // 计算 X 坐标
        $z: math.cos($deg) * $r; // 计算 Z 坐标
        transform: translate3d($x, 0, $z) rotateY(180deg + $deg);
      }
    }
  }
}
// 已知:
// sin(θ)= 对边/斜边
// cos(θ)= 邻边/斜边

备注:旋转切换的轮播图和环绕式照片墙案例都用scss进行了计算,编译:sass styles.scss styles.css查看css代码,明显减少了代码,后面将分享scss的相关属性和方法。


http://www.kler.cn/a/454832.html

相关文章:

  • Linux嵌入式编程中与线程有关的知识(线程的概念、线程的创建、互斥锁、线程挂起、主线程、如何看查线程的ID等知识点)
  • QML自定义DelayButton(带进度的按钮)样式
  • day26 文件io
  • 【elementplus】中文模式
  • Redis 应用场景深度探索
  • Docker部署GitLab服务器
  • docker 容器中没有ping命令和ifconfig命令和wget怎么办?
  • LeetCode-Z 字形变换(006)
  • layui动态拼接生成下拉框验证必填项失效问题
  • 曼哈顿图如何指定不同染色体不同的颜色
  • 【Linux命令】ps -a 和 ps -ef 的区别
  • MySQL 服务正在启动.MySQL 服务无法启动.服务没有报告任何错误。请键入 NET HELPMSG 3534 以获得更多的帮助。总结较全 (已解决)
  • 香港站群服务器如何排查 Linux 系统的内存泄漏问题
  • 远程作业专家指导调度系统
  • 中巨伟业推出高安全高性能32位智能卡内核可编程加密芯片SMEC88SP/ST
  • 通过百度api处理交通数据
  • Java中处理if-else的几种高级方法
  • 用Excel表格在线发布期末考试成绩单
  • USB免驱IC读写器QT小程序开发
  • 计算机网络 (9)数据链路层
  • 深度学习在图像识别中的最新进展与实践案例
  • 如何在 Vue 中处理 API 请求?
  • 第3章 并行循环调度的准则
  • c++ 打开摄像头并显示摄像头捕获的数据
  • 【进阶编程】代理模式和适配模式的比较
  • 【视觉惯性SLAM:八、ORB-SLAM2:特征匹配】