草庐IT

记录--手把手教学,实现一个优雅的图片预览

林恒 2023-03-28 原文

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

在我们开发项目中,经常会遇到预览图片的需求。也就是点击图片,会全屏显示该图片。需求很简单,但是如何让实现更优雅就需要花点心思了。

最终效果图

基础版本

实现方式

  • 点击图片,创建蒙层,克隆图片
  • 将图片添加定位属性,并添加到蒙层中
  • 将蒙层添加到body中

观察下图发现,虽然实现了需求,但是动画很生硬,我们作为前端开发工程师,得对得起工程师的身份,需要有工匠精神,接下来将介绍如何实现优雅的图片预览效果。

效果图

代码

<!DOCTYPE html>
<html lang="en">
<head>
  <title>基础版本</title>
  <style>
    .pic1 {
      width: 400px;
    }
    .mask {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.7);
    }
    .previewImg {
      position: absolute;
      width: 80%;
      left: 50%;
      top: 200px;
      transform: translateX(-50%);
    }
  </style>
</head>
<body>
  <img class="pic1" src="./xtjj.jpg" alt="">
  <script>
    const pic1 = document.querySelector(".pic1");
    pic1.addEventListener("click", function () {
      // 创建蒙层
      const mask = document.createElement("div")
      mask.classList.add("mask");
      const pic1Clone = pic1.cloneNode();
      pic1Clone.classList.add("previewImg");
      // 将图片和蒙层添加到页面中
      mask.appendChild(pic1Clone)
      document.body.appendChild(mask)

      mask.addEventListener("click", function () {
        document.body.removeChild(this)
      })
    })
  </script>
</body>
</html>

打开动画

实现方式

  • 点击图片
    • 克隆原图,计算原图当前距离窗口的top与left的距离(用于确定克隆图片的初始位置)
    • 计算克隆图片的初始位置以及其相关属性
    • 创建蒙层,并添加相关的定位,背景色属性
    • 使用setTimeout是为了触发transition,产生移动效果。
    • 并且在setTimeout最开始将原图进行隐藏,产生是原图移动到屏幕中心的效果。使用visibility属性,避免引起页面布局变化。
  • 为蒙层添加点击事件
    • 点击蒙层后,删除蒙层元素,实现关闭预览功能

效果图

代码

<!DOCTYPE html>
<html lang="en">
<head>
  <title>添加预览动画</title>
  <style>
    .pic {
      width: 400px;
    }
    .mask {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.7);
      transition: all .3s;
    }
    .previewImg {
      position: absolute;
      transform: translateX(-50%);
      transition: all .3s;
    }
  </style>
</head>
<body>
  <img class="pic" src="./xtjj.jpg" alt="">
  <script>
    const pic = document.querySelector(".pic");
    pic.addEventListener("click", function () {
      // 1,克隆元素
      const pic2 = pic.cloneNode();
      // 2,计算原图距离窗口left,top的距离
      picToTop = pic.getBoundingClientRect().x;
      picToLeft = pic.getBoundingClientRect().y;
      // 3,设置克隆图片初始位置
      pic2.style.position = "absolute";
      pic2.style.left = `${picToLeft}px`;
      pic2.style.top = `${picToTop}px`;
      // 4,创建蒙层
      const mask = document.createElement("div")
      mask.classList.add("mask");
      // 5,将元素添加到body中
      mask.appendChild(pic2)
      document.body.appendChild(mask)
      // 6,使用setTimeout是为了触发`transition`,产生动画
      setTimeout(() => {
        // 7,隐藏原图片
        pic.style.visibility = "hidden";
        // 8,设置预览图片展示宽度以及位置
        pic2.style.width = "80%";
        pic2.style.left = "50%";
        pic2.style.top = `200px`;
        pic2.classList.add("previewImg");
      }, 0);
      // 9,点击蒙层关闭预览
      mask.addEventListener("click", function () {
        this.remove()
      })
    })
  </script>
</body>
</html>

关闭动画

上一个步骤中,实现了点击图片,图片流畅显示的动画。但是关闭的时候很突然,这次将实现关闭的流畅动画。

实现方式

  • 点击图片的时候获取原图的宽度,以及距离窗口lefttop的距离
  • 点击蒙层的时候将克隆图片的位置移动到原图的位置(根据前面获取的原图位置)
  • 当克隆图片回到原图的位置时,需要将原图进行显示。使用visibility属性。
  • 使用setTimeout的原因是触发transition,产生动画效果。
  • 使用300毫秒是因为transition设置的是300毫秒的过渡时间,为了能在克隆图片会到原图位置的时候,再显示原图,并删除蒙层。

效果图

代码

查看下个步骤

滚动时取消预览

实现方式

  • 在点击图片的时候,存储当前页面滚动的距离:lastPositon

  • 监听滚动事件,当滚动的距离减去lastPositon的值,大于100px的时候,触发蒙层的点击事件

  • 蒙层收到点击动作后,会执行取消预览的一系列动作。

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
  <title>滚动时取消预览</title>
  <style>
    body {
      height: 1000px;
    }
    .pic {
      width: 400px;
    }
    .mask {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: rgba(0, 0, 0, 0.7);
      transition: all .3s;
    }
  </style>
</head>
<body>
  <img class="pic" src="./xtjj.jpg" alt="">
  <script>
    // 16,滚动超过100px。就取消预览
    window.onscroll = (e) => {
      if (Math.abs(window.pageYOffset - lastPositon) > 100) {
        document.querySelector(".mask")?.click();
      }
    }
    // 14,预览图片前页面滚动距离初始值
    let lastPositon = 0;
    const pic = document.querySelector(".pic");
    pic.addEventListener("click", function () {
      // 15,计算预览图片前页面滚动距离
      lastPositon = window.pageYOffset;
      // 1,克隆元素
      const pic2 = pic.cloneNode();
      // 2,计算原图距离窗口left,top的距离
      picToTop = pic.getBoundingClientRect().x;
      picToLeft = pic.getBoundingClientRect().y;
      // 11,计算原图宽度
      picWidth = pic.width;
      // 3,设置克隆图片初始位置
      pic2.style.position = "absolute";
      pic2.style.left = `${picToLeft}px`;
      pic2.style.top = `${picToTop}px`;
      // 4,创建蒙层
      const mask = document.createElement("div")
      mask.classList.add("mask");
      // 5,将元素添加到body中
      mask.appendChild(pic2)
      document.body.appendChild(mask)
      // 6,使用setTimeout是为了触发`transition`,产生动画
      setTimeout(() => {
        // 7,隐藏原图片
        pic.style.visibility = "hidden";
        // 8,设置预览图片展示宽度以及位置
        pic2.style.position = "absolute";
        pic2.style.transition = "all .3s";
        pic2.style.transform = "translateX(-50%)";
        pic2.style.width = "80%";
        pic2.style.left = "50%";
        pic2.style.top = `200px`;
      }, 0);
      // 9,点击蒙层关闭预览
      mask.addEventListener("click", function () {
        // 10,预览图回到原图位置
        pic2.style.width = `${picWidth}px`;
        pic2.style.left = `${picToLeft}px`;
        pic2.style.top = `${picToTop}px`;
        pic2.style.transform = "";
        // 12,显示原图
        setTimeout(() => {
          pic.style.visibility = "visible";
          // 13,删除蒙层以及预览图
          this.remove()
        }, 300);
      })
    })
  </script>
</body>
</html>

本文转载于:

https://juejin.cn/post/7155789252075356190

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

有关记录--手把手教学,实现一个优雅的图片预览的更多相关文章

  1. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  2. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  3. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  4. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  5. ruby - Sinatra:运行 rspec 测试时记录噪音 - 2

    Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/

  6. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  7. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  8. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  9. ruby-on-rails - Rails 5 Active Record 记录无效错误 - 2

    我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa

  10. ruby-on-rails - Ruby on Rails - 为文本区域和图片生成列 - 2

    我是Rails的新手,所以请原谅简单的问题。我正在为一家公司创建一个网站。那家公司想在网站上展示它的客户。我想让客户自己管理这个。我正在为“客户”生成一个表格,我想要的三列是:公司名称、公司描述和Logo。对于名称,我使用的是name:string但不确定如何在脚本/生成脚手架终端命令中最好地创建描述列(因为我打算将其设置为文本区域)和图片。我怀疑描述(我想成为一个文本区域)应该仍然是描述:字符串,然后以实际形式进行调整。不确定如何处理图片字段。那么……说来话长:我在脚手架命令中输入什么来生成描述和图片列? 最佳答案 对于“文本”数

随机推荐