首页 > 其他分享 >如何实现抖音 '回到刚刚查看的' 位置

如何实现抖音 '回到刚刚查看的' 位置

时间:2023-06-02 17:58:57浏览次数:32  
标签:function index const 查看 item 抖音 let 刚刚 page

实现效果,假如当前浏览的是第121条视频

 

首先需要先准备要用到的工具函数,和模拟接口回调函数

api.js

//模拟根据当前 id返回 该id前面有多少条视频
export function getOffset(id) {
    return new Promise(resolve => {
        resolve(121)
    })
}



//模拟返回一个具有分页功能的图片列表的函数
export function getVideo(pageNo, pageSize) {
    return new Promise(resolve => {
        // let page = Math.ceil(pageNo/pageSize) //Calculates the number of pages.
        let start = (pageNo - 1) * pageSize; //Calculates the start index. 
        let end = start + pageSize; //Calculates the end index. 
        let arr = []; //Declaring an array. 

        for (let i = start; i < end; i++) { //For loop to add the image to the array. 
            let obj = { id: i, cover: `https://picsum.photos/200/${i}` }
            arr.push(obj)

        }
        resolve(arr) //Resolve the promise.
    })
}

工具函数

utils.js


//防抖
export function debounce(fn, delay = 500) {
    let timer = null;

    return function () {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        }, delay)
    }

}

//根据页码,和数量获取到当前 对应的下标范围
export function getIndexRange(pageNo, pageSize) {
    const start = (pageNo - 1) * pageSize; // -1 to compensate for 0 indexing.  0-0 is 0.  So -
    const end = start + pageSize - 1; // compensate for 0-0 being 0.  So end is the same as start.  So end = start.  So
    return [start, end]

}

//根据下标,和数量,计算出在哪一页
export function getPage(index, size) {
    return Math.ceil((index + 1) / size)
}

 

主要运用逻辑在两个函数

createElement 动态创建列表节点 loadPages  加载对应索引页的内容   主函数页面
import { getOffset, getVideo } from './api.js' 
import { debounce, getIndexRange, getPage } from './utils.js'

const currentId = 100 //当前看过视频的id
const SIZE = 10  //分页的size
const continer = document.querySelector('.continer')
const indicator = document.querySelector('.indicator')


let visibleIndex = new Set() //用于存放出现在视口中元素的 索引值

const ob = new IntersectionObserver(entires => {    //创建一个元素监听器,监听 每个视频的item

    for(const entry of entires){
        const index = +entry.target.dataset.index;
        if(entry.isIntersecting){    // isIntersecting:表示这个元素是否出现在视口中
            visibleIndex.add(index)
        } else {
            visibleIndex.delete(index)
        }
    }
    loadPagesDebounce() //加载这些索引对应的内容
    
})

function getRange() {  //获取视口中索引值集合的 最大和最小
    if(visibleIndex.size === 0) return [0,0]
    const min = Math.min(...visibleIndex)
    const max = Math.max(...visibleIndex)
    return [min, max]
}

function createElement(page) {   //手动创建 需要加载的所有节点
    const childrenLen = continer.children.length
    const count = page * SIZE - childrenLen;
    for (let i = 0; i < count; i++) {
        const item = document.createElement('div')
        item.className = 'item'
        item.dataset.index = i + childrenLen
        continer.appendChild(item)
        ob.observe(item)  //为所有节点添加监听器
    }

}

function loadPages(){
    //得到当前可以看到的索引范围的内容
    const [minIndex, maxIndex] = getRange()
    const pages = new Set()
    for (let i = minIndex; i <= maxIndex; i++) {
        pages.add(getPage(i, SIZE))  //获取当前视口中需要加载的页面集合pages
    }
    for (const page of pages) {
        const [minIndex, maxIndex] = getIndexRange(page, SIZE)
        //   console.log(minIndex, maxIndex)
        if (continer.children[minIndex].dataset.loaded) { //防止已经加载过的重复加载
            continue;
        }
        continer.children[minIndex].dataset.loaded = true

        getVideo(page, SIZE).then(async (res) => {   //循坏模拟获取数据的列表

            for (let i = minIndex; i <= maxIndex; i++) {
                const item = continer.children[i] 	//item is a div element.
                const offset = await getOffset(currentId)
                item.innerHTML = `
                    <img src="${res[i - minIndex].cover}"  />
                    <span style='color:#f00'>${i} ${item.dataset.index == offset ? '---刚刚看过!' : ''}</span>
                `
            }
        })
    }
    setIndicatorVisible()

}

//判断当前位置是否需要展示刚刚看过的按钮
async function setIndicatorVisible(){
    const offset = await getOffset(currentId)
    const [minIndex, maxIndex] = getRange() 	//currentId is a div element.  offset is the top left of the element.  (0,0) is the top left of
    const page = getPage(offset, SIZE)
    // console.log(minIndex, maxIndex, offset)
    if(offset>=minIndex && offset<=maxIndex){
     indicator.style.display = 'none'	//display the indicator element.  (the div element)  (the top left of the element)  (0,
    } else indicator.style.display = 'block'	
 
    indicator.dataset.page = page
    indicator.dataset.index = offset
 }

indicator.onclick = () => {
  const page = +indicator.dataset.page 	//page is a number.  + indicator.dataset.page is a string.  + indicator
  const index = +indicator.dataset.index 	//index is a number.  + indicator.dataset.index is a string
  createElement(page)
  continer.children[index].scrollIntoView({
    behavior:'smooth',
    block:'center'
  })
}

const loadPagesDebounce = debounce(loadPages, 300)

createElement(1) //首次先加载一页
 
html代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<style>
    /* body,html{
        position: relative;
    } */
    .continer{
        display: grid;
        /* grid-gap: 20px; */
        grid-template-columns: 1fr 1fr 1fr;
        grid-column-gap: 10px;
        grid-row-gap: 15px;
        /* flex-wrap: wrap; */
        padding: 0 200px;
        box-sizing: border-box;
    }
    .item{
        width: 200px;
        height: 300px;
        border: 1px solid #000;
        margin-bottom: 20px;
        padding-bottom: 20px;
    }
    .item img{
        width: 200px;
        height: 280px;
        object-fit: cover;
    }
    .indicator{
        position: fixed;
        left: 0;
        right: 0;
        bottom: 20px;
        width: 100px;
        margin: 0 auto;
        text-align: center;
        /* display: none; */
        background: #f00;
        color: #fff; /* for IE */
    }
</style>
<body>
    <div class="continer">
        
    </div>
   <div class="indicator">前往刚刚看过</div>
</body>
<script type="module" src="./loadPages/index.js"></script>
<script type="module" src="./loadPages/api.js"></script>
</html>

 

标签:function,index,const,查看,item,抖音,let,刚刚,page
From: https://www.cnblogs.com/xuhuang/p/17452559.html

相关文章

  • Blender 在UV编辑画面查看UV
     1、点击最上面的UV编辑,切换tab 2、当我们在右侧选中模型查看下面的VV贴图,这里有几条就是几套UV。3、 有两点注意事项。(1)每次重新选中模型展UV都得先切到物体模式再切换到编辑模式。(2)想挨个查看每个模型有几套UV。必须得在物体模式下切换查看。......
  • linux运行jar包、查看jar包进程、停止jar包
    后台运行:nohupjava-jarruoyi-admin.jar>msg.log2>&1&停止:ps-ef|grepXXX.jarkillXXXXXX 1、后台运行jar包程序,输入:nohupjava-jar/路径/程序.jar&2、后台终止jar包程序,输入:ps-ef|grepjava,查看使用java命令的进程,再输入:killpid 即可终止运行------------......
  • linux下查看IP域名端口的网络是否相通命令
     linux查看IP、域名、端口的网络是否相通1.ping#检索当前域名对应的IP地址ping域名#查看IP是否相通pingIP2.tlenet#查看指定IP的端口是否相通,http默认端口为80,https默认端口为443telnetip/域名port3.wget#查看地址是否可以链接wgetip:端口4.nslo......
  • 高分辨率大图像可缩放 Web 查看器的实践
    高分辨率大图像可缩放Web查看器的实践一、使用vips将高分辨率大图像转换为DZI安装vips具体安装步骤请参考libvipsInstall。注意,在windows11中安装v8.14.2版本后,在运行vipsdzsave**.jpgmydz命令时,出现vips:unknownaction"dzsave"报错,解决办法是重装低版......
  • Blender查看统计面数的方法
    1、底部状态栏右键2、在显示叠加层查看统计信息 3、 左上角显示 数据 ......
  • Linux如何查看JDK的安装路径
    whichjava首先要申明一下whichjava是定位不到安装路径的。whichjava定位到的是java程序的执行路径。[root@localhost~]#whichjava/usr/bin/java[root@localhost~]#ls-lrt/usr/bin/javalrwxrwxrwx.1rootroot22Aug1715:12/usr/bin/java->/etc/alternatives......
  • linux下查看 SELinux状态及关闭SELinux
    SELinux全称为安全增强式Security-EnhancedLinux(SELinux),是一个在内核中实践的强制存取控制(MAC)安全性机制。SELinux首先在CentOS4出现,并在其后的CentOS发行版本获得重大改善。这些改善代表用SELinux解决问题的方法亦随著时间而改变。SELinux的原理与架构SELinux的整体......
  • 如何查看redis占用内存大小
    http://www.daixiaorui.com/read/209.html#Memoryused_memory:13490096//数据占用了多少内存(字节)used_memory_human:12.87M//数据占用了多少内存(带单位的,可读性好)used_memory_rss:13490096 //redis占用了多少内存used_memory_peak:15301192//占用内存的峰值(字节)used_memory_p......
  • vsftpd日志配置及查看——可以将vsftpd记录在系统日志里
    vsftpd日志配置及查看vsftpdftp服务器的日志设置,可以通过修改主配置文件/etc/vsftpd.conf实现。主配置文件中与日志设置有关的选项包括xferlog_enable、xferlog_file和dual_log_enable等。 xferlog_enable 如果启用该选项,系统将会维护记录服务器上传和下载情况的日志文件。默......
  • 信创操作系统–麒麟Kylin桌面操作系统 (项目十一 使用终端及查看硬件信息)
    bash与终端什么是bash操作系统的内核(Kernel)管理着整台计算机的硬件,是操作系统中最基本的部分。内核处于系统的底层,是不能让普通用户随意操作的,以避免误操作导致系统崩溃。这就需要一个专门的程序,让它接受用户输入的命令,然后根据指令去调用对应程序,接着该应用程序直接与内核沟通,......