为自己的项目媒体资源添加固定高度

为自己的项目媒体资源添加固定高度

未媒体资源添加固定高度,不仅有利于确定懒加载后的切确位置,还可以做骨架屏、loading动画等等,但是因为历史数据中很多没有加高度的媒体资源,所以一直嫌麻烦没有做。

直到这个季度有一个自上而下(不可抗力)的push。一个需求需要在懒加载的情况下跳转到底部的一个坐标。

一开始我们拟定的方案是中途查询,边查询边修改目标高度,但是这样做无法避免有些很大的图片加载慢的情况,跳转到正确位置后整整1~2秒才加载出来,这时候我们已经跳转到目标地在那里正常浏览了,页面突然被顶下去的效果是我们无法接受的。(尤其是在移动端真机测试上表现得更加明显)。

于是不得已之下必须还是得选择固定高度的方案,翻找了一下网上的建议,最后找到了一个比较快捷的方式如下,只需要设定好每个媒体资源的宽高比例,以及宽度,即可自动设定好高度。
根据padding-bottom配置比例。
图片和视频资源都需要配置固定高度,以前一直以为视频是有高度的,但是在精确测试后发现视频未加载时候的高度和它加载后的高度不匹配。

把懒加载组件和中途查询函数的代码列如下方

图片固定高度组件

需注意:因为固定高度的span包裹在lazyload里面,所以它仍然会用户下滑到某个距离才显示出内部内容 以及 高度,但是span \ padding-bottom的加载速度极快,比媒体资源快很多,所以可以达到下滑时看到固定高度的效果。
但是要达到我上面的需求:锚点定位跳转,可能还是会有定位不准的情况,因此我采用两种方式相结合,也就是中途查询+固定高度。

import React, { useEffect } from 'react';
import LazyLoad from 'react-lazyload';
import ossImgCompress from '@/utils/ossImgCompress';
import styles from './index.module.less';/*** @param {string} className* @param {Array} ratio [长,宽]图片比例,设置比例后必须设定懒加载组件className或者outerStyle中的宽度。* @param {Object} outerStyle* @returns*/
const LazyLoadImg = ({ children, ...props }) => {let {src,offset = 400,ratio,className,coverClassName,outerStyle = {},style = { width: '100%' },onClick,webp,type = 'img',key,...restProps} = props;src = webp ? ossImgCompress(src, { webp }) : src;const hasRatio = Array.isArray(ratio) && type !== 'cover';return (<LazyLoad offset={offset} className={[styles.lazyimg, className].join(' ')} style={outerStyle}>{hasRatio && <span className={styles.ratioSpan} style={{ paddingBottom: `${(ratio[1] / ratio[0]) * 100}%` }} />}{type === 'img' && <img {...restProps} src={src} style={style} onClick={onClick} alt='' />}{type === 'cover' && (<div{...restProps}className={coverClassName ?? styles.cover}style={{ backgroundImage: `url(${src})`, ...style }}onClick={onClick}>{children}</div>)}</LazyLoad>);
};
export default LazyLoadImg;
// css.cover {width: 100%;height: 100%;background-size: cover;background-position: center;background-repeat: no-repeat;
}.lazyimg {position: relative;font-size: 0;line-height: 0;overflow: hidden;.ratioSpan {position: unset !important;display: inline-block;box-sizing: border-box;width: initial;height: initial;background: none;opacity: 0;border: 0px !important;margin: 0px !important;& + img {border-radius: inherit;position: absolute;inset: 0px;box-sizing: border-box;padding: 0px;border: none;margin: auto;display: block;width: 0px;height: 0px;min-width: 100%;max-width: 100%;min-height: 100%;max-height: 100%;}}
}
视频固定高度组件

视频的懒加载函数既是将data-src的内容放到src里,这个函数在其他文章《如何优化一个很多视频的网页》中给出了。
video的固定高度放在了lazyload组件上,所以高度会一直存在,基本不存在图片固定高度组件中 滑动一定距离才加载出内部元素 & 高度的情况。

import React from 'react';
import LazyLoad from 'react-lazyload';
import styles from './index.module.less';/*** 存在两种懒加载方案、可以自动设置高度的video组件* @param {Object} style video元素的style* @param {Object} outerStyle video元素父级组件的style* @param  src 采用LazyLoad 懒加载组件加载* @param  poster 采用LazyLoad 懒加载组件加载* @param  data_src 不采用懒加载组件(否则获取不到video元素),配合VideoLazyLoad使用* @param  data_poster 不采用懒加载组件,配合VideoLazyLoad使用* @param  offset* @param {Array} ratio 视频宽高比例,设置后需要设定父级className的宽度* @param  className* @param  autoPlay* @param  onClick* @param {object} videoProps* @returns*/
const LazyLoadVideo = ({ children, ...props }) => {let {src,poster,data_src,data_poster,offset = 400,ratio,className,autoPlay = true,onClick,videoProps,outerStyle = {},style = { width: '100%' },key,} = props;return data_src ? (<div key={key} className={[styles.lazyVideo1, className].join(' ')} style={outerStyle}><spanclassName={styles.ratioSpan}style={{ paddingBottom: Array.isArray(ratio) && `${(ratio[1] / ratio[0]) * 100}%` }}/><videodata-src={data_src}data-poster={data_poster || poster}poster={poster}src={src}style={style}disablePictureInPictureautoPlay={autoPlay}loopmutedplaysInlinecontrols={false}onClick={onClick}{...videoProps}/></div>) : (<LazyLoadkey={key}offset={offset}className={[styles.lazyVideo, className].join(' ')}style={{ ...outerStyle, paddingBottom: Array.isArray(ratio) ? `${(ratio[1] / ratio[0]) * 100}%` : 'auto' }}><videosrc={src}style={style}poster={poster}disablePictureInPictureautoPlay={autoPlay}loopmutedplaysInlinecontrols={false}onClick={onClick}{...videoProps}/></LazyLoad>);
};
export default LazyLoadVideo;
// css
.lazyVideo {position: relative;width: 100%;height: 0;overflow: hidden;display: inline-block;box-sizing: border-box;background: none;border: 0px;margin: 0px;font-size: 0;line-height: 0;video {object-fit: fill;position: absolute;inset: 0px;box-sizing: border-box;padding: 0px;border: none;margin: 0 auto;display: block;width: 100%;height: 100%;font-size: 0;line-height: 0;border-radius: inherit;}
}.lazyVideo1 {position: relative;font-size: 0;line-height: 0;.ratioSpan {width: 100%;inset: 0px;height: 0;overflow: hidden;display: inline-block;box-sizing: border-box;background: none;border: 0px;margin: 0px;font-size: 0;line-height: 0;}video {object-fit: fill;position: absolute;inset: 0px;box-sizing: border-box;padding: 0px;border: none;margin: 0 auto;display: block;width: 100%;height: 100%;border-radius: inherit;}
}
中途查询修改目标高度的函数

其实JQUERY似乎是有一个$().animation()函数已经直接实现了这个功能,但是因为我的项目没有用到,所以就只能自己实现一下了。

const targetRef = useRef(null);
const menuHeader = useRef(null);
const timer = useRef(null);
const max_call = 10;
let max_call_count = 0;
const [res, setRes] = useState([]);<button onClick={handleScrollToTarget}>跳转</button>const scrollToElement = async (lastScrollTop, lastWindowScrollY, lastElementTop) => {// 安卓手机端兼容性const windowScrollY = document.documentElement.scrollTop || document.body.scrollTop;const nowElementTop = targetRef.current.offsetTop;clearTimeout(timer.current);timer.current = null;const margin = isMobile ? (menuHeader.current ? menuHeader.current.clientHeight : 80) : 50;const target = nowElementTop - margin;const step1 = nowElementTop / 3;const step2 = nowElementTop * 0.75;let scrollTop = 0; // 记录本次指定的高度,避免反复赋值造成卡顿// 分阶段赋予高度,避免过快跳转导致中间有高度变化查询不到if (windowScrollY < step1 - 300) {scrollTop = step1;}if (windowScrollY >= step1 - 300 && windowScrollY < step2 - 300) {scrollTop = step2;}if (windowScrollY >= step2 - 300) {scrollTop = target;}if (isReachBottom(10)) {max_call_count = 0;return window.scrollTo(0, targetRef.current.offsetTop);}if (targetRef.current.getBoundingClientRect().top.toFixed(0) > margin) {// 判断当前高度 避免反复赋值造成卡顿if (windowScrollY >= lastScrollTop - 300 || nowElementTop.toFixed(0) !== lastElementTop.toFixed(0)) {window.scrollTo({top: scrollTop,left: 0,behavior: 'instant',});}// 每100ms更新一次if (max_call_count < max_call) {if (lastWindowScrollY === windowScrollY) max_call_count += 1;timer.current = setTimeout(() => {scrollToElement(scrollTop, windowScrollY, nowElementTop);}, 90);}}};// 避免懒加载一直加载导致屏幕滚动失败,将路程分为4段,每隔一段查询一次目标高度,直到滚动到目标位置const handleScrollToTarget = async () => {try {// 查询目标let firstScrollTop = 0;if (!targetRef.current) {const t = await querySelector('#target').then((ele) => {targetRef.current = ele;return ele;});if (!t) return;firstScrollTop = t.offsetTop;} else {firstScrollTop = targetRef.current.offsetTop;}// 查询documentElement & 错误捕获const documentElement =document.documentElement ||(await new Promise((resolve, reject) => {const interval = setInterval(() => {if (max_call_count < max_call) {if (!!document.documentElement) {max_call_count = 0;clearInterval(interval);resolve(document.documentElement);} else {max_call_count++;}} else {clearInterval(interval);max_call_count = 0;resolve(null);}}, 100);}));if (documentElement || document.documentElement) {window.scrollTo(0, firstScrollTop);scrollToElement(firstScrollTop / 2, 0, firstScrollTop);} else {throw Error('无法获取document.documentElement');}} catch (e) {console.error('滚动函数失败——', e);// 兜底函数,直接滚动}};/*** 页面触底判断* @param margin 触底函数判断范围*/
function isReachBottom(margin = 36) {// 窗口高度var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;// 滚动高度var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;// 页面高度var documentHeight = document.documentElement.scrollHeight || document.body.scrollHeight;if (windowHeight + scrollTop + margin > documentHeight) {return true;} else {return false;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/680426.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯每日一练(python)B组

###来源于dotcpp的蓝桥杯真题 题目 2735: 蓝桥杯2022年第十三届决赛真题-取模&#xff08;Python组&#xff09; 给定 n, m &#xff0c;问是否存在两个不同的数 x, y 使得 1 ≤ x < y ≤ m 且 n mod x n mod y 。 输入格式&#xff1a; 输入包含多组独立的询问。 第一…

关于在分布式环境中RVN和使用场景的介绍3

简介 在《关于在分布式环境中RVN和使用场景的介绍2》和《关于在分布式环境中RVN和使用场景的介绍1》中我们介绍了RVN的概念和在一些具体用例中的使用。在本文中我们讨论一下在分布式环境中使用RVN需要注意的问题。 问题 我们在收到一条待处理的事件时&#xff0c;需要检查该…

绝大部分人都不知道如何鉴定Oracle OCP/OCM和MySQL OCP证书的真伪

知道如何鉴定自己的Oracle OCP/OCM和MySQL OCP证书的真伪很重要&#xff0c;因为目前的IT证书基本都是电子的&#xff0c;很少有纸质的证书。如果要验证这些电子证书的真伪&#xff0c;通常可以到发证机构的网站输入证书ID号进行查询。Oracle公司的Oracle和MySQL数据库的OCP/OC…

springboot173疫苗发布和接种预约系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

FT2232调试记录(1)

FT2232调试记录 &#xff08;1&#xff09;获取当前连接的FTDI设备通道个数:&#xff08;2&#xff09;获取当前连接的设备通道的信息:&#xff08;3&#xff09;配置SPI的通道:&#xff08;4&#xff09;如何设置GPIO:&#xff08;5&#xff09;DEMO测试&#xff1a; #参考文档…

全坚固平板EM-I12U,全新升级后的优质体验

平板终端机在户外勘探、制造业、畜牧业、银行金融行业当中都不是陌生的&#xff0c;能采集各种数据来转换成信息流向企业和行业的各个分支当中&#xff0c;在整个行业发展、社会推动上面都起着关键性作用&#xff0c;而平板终端机的升级也就意味着未来的这些行业发展会进入一个…

【Python】CRAPS赌博游戏

说明&#xff1a;CRAPS又称花旗骰&#xff0c;是美国拉斯维加斯非常受欢迎的一种的桌上赌博游戏。该游戏使用两粒骰子&#xff0c;玩家通过摇两粒骰子获得点数进行游戏。简单的规则是&#xff1a;玩家第一次摇骰子如果摇出了7点或11点&#xff0c;玩家胜&#xff1b;玩家第一次…

闲来无事,写几个好看的产品宣传界面,希望您喜欢

闲来无事&#xff0c;再写几个产品宣传页 宣传页面一&#xff1a; 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&…

JavaScript进阶教程 - Vue(Composition API、Vuex)

Vue.js 是一个渐进式 JavaScript 框架,用于构建用户界面。Vue 的核心库只关注视图层,易于学习且易于集成。随着 Vue 3 的发布,引入了 Composition API,这是一种新的组件编写方式,旨在解决 Vue 2 中使用 Options API 时遇到的一些限制。此外,Vuex 是 Vue 应用的状态管理模…

分享88个jQuery特效,总有一款适合您

分享88个jQuery特效&#xff0c;总有一款适合您 88个jQuery特效下载链接&#xff1a;https://pan.baidu.com/s/1NKQfcdNcojvA8xAb0BCaRA?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理…

探索Redis特殊数据结构:Geospatial(地理位置)在实际中的应用

一、概述 Redis官方提供了多种数据类型&#xff0c;除了常见的String、Hash、List、Set、zSet之外&#xff0c;还包括Stream、Geospatial、Bitmaps、Bitfields、Probabilistic&#xff08;HyperLogLog、Bloom filter、Cuckoo filter、t-digest、Top-K、Count-min sketch、Confi…

ES实战-分析数据1

分析是文档被发送并加入倒排索引之前,es在其主体上进行的操作,具体如下 1.字符过滤-使用字符过滤器转变字符 2.文本切分为分词-将文本切分为单个或多个分词 3,分词过滤-使用分词过滤器转变每个分词 4.分词索引-将这些分词存储到索引中 为文档使用分析器 1.当创建索引的时候,为特…

二、docker compose安装

docker compose安装 docker compose的所有版本&#xff1a;https://github.com/docker/compose/releases # 安装步骤 # 1.下载docker compose&#xff1a;v2.5.0是docker-compose版本 curl -L https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-lin…

BIO、NIO、Netty演化总结

关于BIO&#xff08;关于Java NIO的的思考-CSDN博客&#xff09;和NIO&#xff08;关于Java NIO的的思考-CSDN博客&#xff09;在之前的博客里面已经有详细的讲解&#xff0c;这里再总结一下最近学习netty源码的的心得体会 在之前的NIO博客中我们知道接受客户端连接和IO事件的…

每日OJ题_位运算④_力扣268. 丢失的数字

目录 力扣268. 丢失的数字 解析代码 力扣268. 丢失的数字 268. 丢失的数字 难度 简单 给定一个包含 [0, n] 中 n 个数的数组 nums &#xff0c;找出 [0, n] 这个范围内没有出现在数组中的那个数。 示例 1&#xff1a; 输入&#xff1a;nums [3,0,1] 输出&#xff1a;2 …

数字图像处理技术

源码在末尾 ————————————————————————— 材料 有需要源码找我

【C语言】(21)非局部跳转库setjmp

setjmp 库提供了在 C 程序中进行非局部跳转的机制&#xff0c;它主要由两个函数组成&#xff1a;setjmp 和 longjmp。这两个函数通常用于异常处理和程序控制流的改变&#xff0c;尤其在错误恢复过程中非常有用。这种机制允许程序从深层嵌套的函数调用中跳转回到一个预先指定的恢…

ZigBee学习——BDB

✨本博客参考了善学坊的教程&#xff0c;并总结了在实现过程中遇到的问题。 善学坊官网 文章目录 一、BDB简介二、BDB Commissioning Modes2.1 Network Steering2.2 Network Formation2.3 Finding and Binding&#xff08;F & B&#xff09;2.4 Touchlink 三、BDB Commissi…

[C/C++] -- CMake使用

CMake&#xff08;Cross-platform Make&#xff09;是一个开源的跨平台构建工具&#xff0c;用于自动生成用于不同操作系统和编译器的构建脚本。它可以简化项目的构建过程&#xff0c;使得开发人员能够更方便地管理代码、依赖项和构建设置。 CMake 使用一个名为 CMakeLists.tx…