为自己的项目媒体资源添加固定高度
为自己的项目媒体资源添加固定高度
未媒体资源添加固定高度,不仅有利于确定懒加载后的切确位置,还可以做骨架屏、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;}
}相关文章:
为自己的项目媒体资源添加固定高度
为自己的项目媒体资源添加固定高度 未媒体资源添加固定高度,不仅有利于确定懒加载后的切确位置,还可以做骨架屏、loading动画等等,但是因为历史数据中很多没有加高度的媒体资源,所以一直嫌麻烦没有做。 直到这个季度有一个自上而…...
家政小程序系统源码开发:引领智能生活新篇章
随着科技的飞速发展,小程序作为一种便捷的应用形态,已经深入到我们生活的方方面面。尤其在家庭服务领域,家政小程序的出现为人们带来了前所未有的便利。它不仅简化了家政服务的流程,提升了服务质量,还为家政服务行业注…...
多表查询
目录 统计出一张数据表中的数据量 查询 dept 表中的数据量 查询 emp 表中的数据量 实现 emp 与 dept 的多表查询 笛卡尔积 消除笛卡尔积 把数据表 emp 的别名定为 e,数据表 dept 的别名定为 d,然后在查询中分别使用 e 和 d 代替这两个表 Oracle从…...
PHP开发日志 ━━ 深入理解三元操作与一般条件语句的不同
概况 三元运算符的功能与“if…else”流程语句一致。 在一般情况下,三元操作替换if条件语句可以精简代码,并且更为直观,但是在下面的情况中使用三元操作将会返回警告。 借图: 案例 比如原代码: class classA{publ…...
多维时序 | Matlab实现RF-Adaboost随机森林结合Adaboost多变量时间序列预测
多维时序 | Matlab实现RF-Adaboost随机森林结合Adaboost多变量时间序列预测 目录 多维时序 | Matlab实现RF-Adaboost随机森林结合Adaboost多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现RF-Adaboost随机森林结合Adaboost多变量时间序列预…...
vue3-内置组件-Suspense
Suspense (实验性功能) <Suspense> 是一项实验性功能。它不一定会最终成为稳定功能,并且在稳定之前相关 API 也可能会发生变化。 <Suspense> 是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌…...
Rust入门:如何在windows + vscode中关闭程序codelldb.exe
在windows中用vscode单步调试rust程序的时候,发现无论是按下stop键,还是运行完程序,调试器codelldb.exe一直霸占着主程序不退出,如果此时对代码进行修改,后续就没法再编译调试了。 目前我也不知道要怎么处理这个事&am…...
git错误整理
remote: Support for password authentication was removed on August 13, 2021. 参考:这篇即可 GnuTLS recv error (-110): The TLS connection was non-properly terminated. 执行下面的指令: git config --global http.sslVerify false...
跟着cherno手搓游戏引擎【22】CameraController、Resize
前置: YOTO.h: #pragma once//用于YOTO APP#include "YOTO/Application.h" #include"YOTO/Layer.h" #include "YOTO/Log.h"#include"YOTO/Core/Timestep.h"#include"YOTO/Input.h" #include"YOTO/KeyCod…...
微信小程序(四十二)wechat-http拦截器
注释很详细,直接上代码 上一篇 新增内容: 1.wechat-http请求的封装 2.wechat-http请求的拦截器的用法演示 源码: utils/http.js import http from "wechat-http"//设置全局默认请求地址 http.baseURL "https://live-api.ith…...
tomcat部署zrlog
1.下载zrlog包,并添加到虚拟机中 1)进入/opt/apache-tomcat-8.5.90/webapps目录 cd /opt/apache-tomcat-8.5.90/webapps2)下载zrlog包 wget http://dl.zrlog.com/release/zrlog-1.7.1-baaecb9-release.war 3)重命名包 mv zrlog-1.7.1-baaecb9-release zrblog 2…...
Ubuntu Desktop 开机数字小键盘
Ubuntu Desktop 开机数字小键盘 1. 开机数字小键盘References 1. 开机数字小键盘 一般情况下,Ubuntu 开机后小键盘区是控制方向键而非数字键,每次开机后若用到数字键都需要按下 NumLock 键。 References [1] Yongqiang Cheng, https://yongqiang.blog…...
树莓派编程基础与硬件控制
1.编程语言 Python 是一种泛用型的编程语言,可以用于大量场景的程序开发中。根据基于谷歌搜 索指数的 PYPL(程序语言流行指数)统计,Python 是 2019 年 2 月全球范围内最为流行 的编程语言 相比传统的 C、Java 等编程语言&#x…...
autojs通过正则表达式获取带有数字的text内容
视频连接 视频连接 参考 参考 var ctextMatches(/\d/).findOne()console.log("当前金币"c.text()) // 获取当前金币UiSelector.textMatches(reg) reg {string} | {Regex} 要满足的正则表达式。 为当前选择器附加控件"text需要满足正则表达式reg"的条件。 …...
Android java基础_类的继承
一.Android Java基础_类的继承 先封装一个persion类,在persion的基础上定义Student类,并基础persion类。 子类能访问父类的成员函数。 class Person {private int age;public void setAge(int age) {if (age < 0 || age > 200)age 0;else {thi…...
nginx stream proxy 模块的ssl连接源码分析
目录 1. 源起2. 分析验证环境的配置3. 源码分析3.1 代理模块的请求入口点分析3.2 发起与上游服务器的连接3.3 连接回调3.4 TCP连接建立成功后为上下游数据透传做准备3.5 TCP连接的ssl上下文初始化3.6 ssl握手成功后的处理3.7 连接数据的收与发1. 源起 我一直来对ssl建立连接的过…...
C#面:Static Nested Class 和 Inner Class 有什么不同
这是两种不同的类嵌套方式。 Static Nested Class : 是一个静态嵌套类,它是在外部类中定义的一个静态类。它可以访问外部类的静态成员和方法,但不能直接访问外部类的非静态成员和方法。静态嵌套类可以独立于外部类实例化,即可以…...
LeetCode、208. 实现 Trie (前缀树)【中等,自定义数据结构】
文章目录 前言LeetCode、208. 实现 Trie (前缀树)【中等,自定义数据结构】题目链接与分类思路 资料获取 前言 博主介绍:✌目前全网粉丝2W,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领…...
java数据结构与算法刷题-----LeetCode151. 反转字符串中的单词
java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 解题思路 这道题,可以理解为,将字符串颠倒…...
《Java 简易速速上手小册》第8章:Java 性能优化(2024 最新版)
文章目录 8.1 性能评估工具 - 你的性能探测仪8.1.1 基础知识8.1.2 重点案例:使用 VisualVM 监控应用性能8.1.3 拓展案例 1:使用 JProfiler 分析内存泄漏8.1.4 拓展案例 2:使用 Gatling 进行 Web 应用压力测试 8.2 JVM 调优 - 魔法引擎的调校8…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
