加载图片是提高用户体验的一个很好方法。图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度。
这对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速、无缝地发布,也可帮助用户在浏览你网站内容时获得更好的用户体验。本文将分享三个不同的预加载技术,来增强网站的性能与可用性。
方法一:用css和JavaScript实现预加载
实现预加载图片有很多方法,包括使用css、JavaScript及两者的各种组合。这些技术可根据不同设计场景设计出相应的解决方案,十分高效。
单纯使用CSS,可容易、高效地预加载图片,代码如下:
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }
#preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }
#preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }
将这三个ID选择器应用到(X)html元素中,我们便可通过CSS的background属性将图片预加载到屏幕外的背景上。
只要这些图片的路径保持不变,当它们在Web页面的其他地方被调用时,浏览器就会在渲染过程中使用预加载(缓存)的图片。简单、高效,不需要任何JavaScript。
该方法虽然高效,但仍有改进余地。使用该法加载的图片会同页面的其他内容一起加载,增加了页面的整体加载时间。
为了解决这个问题,我们增加了一些JavaScript代码,来推迟预加载的时间,直到页面加载完毕。代码如下:
function preloader() {
if (document.getElementById) {
document.getElementById("preload-01").style.background="url(http://domain.tld/image-01.png) no-repeat -9999px -9999px";
document.getElementById("preload-02").style.background="url(http://domain.tld/image-02.png) no-repeat -9999px -9999px";
document.getElementById("preload-03").style.background="url(http://domain.tld/image-03.png) no-repeat -9999px -9999px";
}
}
function addLoadEvent(func) {
var oldonload=window.onload;
if (typeof window.onload !='function') {
window.onload=func;
} else {
window.onload=function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
在该脚本的第一部分,我们获取使用类选择器的元素,并为其设置了background属性,以预加载不同的图片。
该脚本的第二部分,我们使用addLoadEvent()函数来延迟preloader()函数的加载时间,直到页面加载完毕。
如果JavaScript无法在用户的浏览器中正常运行,会发生什么?很简单,图片不会被预加载,当页面调用图片时,正常显示即可。
上述方法有时确实很高效,但我们逐渐发现它在实际实现过程中会耗费太多时间。相反,我更喜欢使用纯JavaScript来实现图片的预加载。
下面将提供两种这样的预加载方法,它们可以很漂亮地工作于所有现代浏览器之上。
JavaScript代码段1
只需简单编辑、加载所需要图片的路径与名称即可,很容易实现:
<div class="hidden">
<script type="text/javascript">
var images=new Array()
function preload() {
for (i=0; i < preload.arguments.length; i++) {
images[i]=new Image()
images[i].src=preload.arguments[i]
}
}
preload(
"http://domain.tld/gallery/image-001.jpg",
"http://domain.tld/gallery/image-002.jpg",
"http://domain.tld/gallery/image-003.jpg"
)
</script>
</div>
该方法尤其适用预加载大量的图片。我的画廊网站使用该技术,预加载图片数量达50多张。将该脚本应用到登录页面,只要用户输入登录帐号,大部分画廊图片将被预加载。
JavaScript代码段2
该方法与上面的方法类似,也可以预加载任意数量的图片。将下面的脚本添加入任何Web页中,根据程序指令进行编辑即可。
<div class="hidden">
<script type="text/javascript">
if (document.images) {
img1=new Image();
img2=new Image();
img3=new Image();
img1.src="http://domain.tld/path/to/image-001.gif";
img2.src="http://domain.tld/path/to/image-002.gif";
img3.src="http://domain.tld/path/to/image-003.gif";
}
</script>
</div>
正如所看见,每加载一个图片都需要创建一个变量,如“img1=new Image();”,及图片源地址声明,如“img3.src=“../path/to/image-003.gif”;”。参考该模式,你可根据需要加载任意多的图片。
我们又对该方法进行了改进。将该脚本封装入一个函数中,并使用 addLoadEvent(),延迟预加载时间,直到页面加载完毕。
function preloader() {
if (document.images) {
var img1=new Image();
var img2=new Image();
var img3=new Image();
img1.src="http://domain.tld/path/to/image-001.gif";
img2.src="http://domain.tld/path/to/image-002.gif";
img3.src="http://domain.tld/path/to/image-003.gif";
}
}
function addLoadEvent(func) {
var oldonload=window.onload;
if (typeof window.onload !='function') {
window.onload=func;
} else {
window.onload=function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
上面所给出的方法似乎不够酷,那现在来看一个使用Ajax实现图片预加载的方法。该方法利用DOM,不仅仅预加载图片,还会预加载CSS、JavaScript等相关的东西。使用Ajax,比直接使用JavaScript,优越之处在于JavaScript和CSS的加载不会影响到当前页面。该方法简洁、高效。
window.onload=function() {
setTimeout(function() {
// XHR to request a js and a CSS
var xhr=new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.js');
xhr.send('');
xhr=new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.css');
xhr.send('');
// preload image
new Image().src="http://domain.tld/preload.png";
}, 1000);
};
上面代码预加载了“preload.js”、“preload.css”和“preload.png”。1000毫秒的超时是为了防止脚本挂起,而导致正常页面出现功能问题。
下面,我们看看如何用JavaScript来实现该加载过程:
window.onload=function() {
setTimeout(function() {
// reference to <head>
var head=document.getElementsByTagName('head')[0];
// a new CSS
var css=document.createElement('link');
css.type="text/css";
css.rel="stylesheet";
css.href="http://domain.tld/preload.css";
// a new JS
var js=document.createElement("script");
js.type="text/javascript";
js.src="http://domain.tld/preload.js";
// preload JS and CSS
head.appendChild(css);
head.appendChild(js);
// preload image
new Image().src="http://domain.tld/preload.png";
}, 1000);
};
这里,我们通过DOM创建三个元素来实现三个文件的预加载。正如上面提到的那样,使用Ajax,加载文件不会应用到加载页面上。从这点上看,Ajax方法优越于JavaScript。
- End -
懒加载其实就是延迟加载,是一种对网页性能优化可方式,比如当访问一个页面的时候,优先显示可视区域的图片而不一次性加载所有图片,当需要显示的时候再发送图片请求,避免打开网页时加载过多资源。
当页面中需要一次性载入很多图片的时候,往往都是需要用懒加载的。
我们都知道HTML中的 <img>标签是代表文档中的一个图像。。说了个废话。。
<img>标签有一个属性是 src,用来表示图像的URL,当这个属性的值不为空时,浏览器就会根据这个值发送请求。如果没有 src属性,就不会发送请求。
嗯?貌似这点可以利用一下?
我先不设置 src,需要的时候再设置?
nice,就是这样。
我们先不给 <img>设置 src,把图片真正的URL放在另一个属性 data-src中,在需要的时候也就是图片进入可视区域的之前,将URL取出放到 src中。
<div class="container">
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img1.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img2.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img3.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img4.png">
</div>
<div class="img-area">
<img class="my-photo" alt="loading" data-src="./img/img5.png">
</div>
</div>
仔细观察一下, <img>标签此时是没有 src属性的,只有 alt和 data-src属性。
alt 属性是一个必需的属性,它规定在图像无法显示时的替代文本。 data-* 全局属性:构成一类名称为自定义数据属性的属性,可以通过 HTMLElement.dataset来访问。
方法一
网上看到好多这种方法,稍微记录一下。
然后判断②-③<①是否成立,如果成立,元素就在可视区域内。
方法二(推荐)
通过 getBoundingClientRect()方法来获取元素的大小以及位置,MDN上是这样描述的:
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
这个方法返回一个名为 ClientRect的 DOMRect对象,包含了 top、 right、 botton、 left、 width、 height这些值。
MDN上有这样一张图:
可以看出返回的元素位置是相对于左上角而言的,而不是边距。
我们思考一下,什么情况下图片进入可视区域。
假设 constbound=el.getBoundingClientRect();来表示图片到可视区域顶部距离; 并设 constclientHeight=window.innerHeight;来表示可视区域的高度。
随着滚动条的向下滚动, bound.top会越来越小,也就是图片到可视区域顶部的距离越来越小,当 bound.top===clientHeight时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。
也就是说,在 bound.top<=clientHeight时,图片是在可视区域内的。
我们这样判断:
function isInSight(el) {
const bound=el.getBoundingClientRect();
const clientHeight=window.innerHeight;
//如果只考虑向下滚动加载
//const clientWidth=window.innerWeight;
return bound.top <=clientHeight + 100;
}
这里有个+100是为了提前加载。
页面打开时需要对所有图片进行检查,是否在可视区域内,如果是就加载。
function checkImgs() {
const imgs=document.querySelectorAll('.my-photo');
Array.from(imgs).forEach(el=> {
if (isInSight(el)) {
loadImg(el);
}
})
}
function loadImg(el) {
if (!el.src) {
const source=el.dataset.src;
el.src=source;
}
}
这里应该是有一个优化的地方,设一个标识符标识已经加载图片的index,当滚动条滚动时就不需要遍历所有的图片,只需要遍历未加载的图片即可。
在类似于滚动条滚动等频繁的DOM操作时,总会提到“函数节流、函数去抖”。
所谓的函数节流,也就是让一个函数不要执行的太频繁,减少一些过快的调用来节流。
基本步骤:
function throttle(fn, mustRun=500) {
const timer=null;
let previous=null;
return function() {
const now=new Date();
const context=this;
const args=arguments;
if (!previous){
previous=now;
}
const remaining=now - previous;
if (mustRun && remaining >=mustRun) {
fn.apply(context, args);
previous=now;
}
}
}
这里的 mustRun就是调用函数的时间间隔,无论多么频繁的调用 fn,只有 remaining>=mustRun时 fn才能被执行。
可以看出此时仅仅是加载了img1和img2,其它的img都没发送请求,看看此时的浏览器
第一张图片是完整的呈现了,第二张图片刚进入可视区域,后面的就看不到了~
当我向下滚动,此时浏览器是这样
此时第二张图片完全显示了,而第三张图片显示了一点点,这时候我们看看请求情况
img3的请求发出来,而后面的请求还是没发出~
当滚动条滚到最底下时,全部请求都应该是发出的,如图
方法三 IntersectionObserver
经大佬提醒,发现了这个方法
先附上链接:
jjc大大:
https://github.com/justjavac/the-front-end-knowledge-you-may-dont-know/issues/10
阮一峰大大:
http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
API Sketch for Intersection Observers:
https://github.com/WICG/IntersectionObserver
IntersectionObserver可以自动观察元素是否在视口内。
var io=new IntersectionObserver(callback, option);
// 开始观察
io.observe(document.getElementById('example'));
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();
callback的参数是一个数组,每个数组都是一个 IntersectionObserverEntry对象,包括以下属性:
属性描述time可见性发生变化的时间,单位为毫秒rootBounds与getBoundingClientRect()方法的返回值一样boundingClientRect目标元素的矩形区域的信息intersectionRect目标元素与视口(或根元素)的交叉区域的信息intersectionRatio目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0target被观察的目标元素,是一个 DOM 节点对象
我们需要用到 intersectionRatio来判断是否在可视区域内,当 intersectionRatio>0&&intersectionRatio<=1即在可视区域内。
代码
function checkImgs() {
const imgs=Array.from(document.querySelectorAll(".my-photo"));
imgs.forEach(item=> io.observe(item));
}
function loadImg(el) {
if (!el.src) {
const source=el.dataset.src;
el.src=source;
}
}
const io=new IntersectionObserver(ioes=> {
ioes.forEach(ioe=> {
const el=ioe.target;
const intersectionRatio=ioe.intersectionRatio;
if (intersectionRatio > 0 && intersectionRatio <=1) {
loadImg(el);
}
el.onload=el.onerror=()=> io.unobserve(el);
});
});
源自:segmentfault
声明:文章著作权归作者所有,如有侵权,请联系小编删除。
JavaScript中,您可以使用HTML5的<input type="file">元素来实现图片上传功能。
以下是一个简单的示例代码,演示如何在JavaScript中上传图片:
HTML部分:
<input type="file" id="uploadInput">
<button onclick="uploadImage()">上传图片</button>
JavaScript部分:
function uploadImage() {
var fileInput=document.getElementById('uploadInput');
var file=fileInput.files[0];
if (file) {
var formData=new FormData();
formData.append('image', file);
// 发送图片数据到服务器
// 这里可以使用XMLHttpRequest或fetch等方法发送请求
// 请根据您的需求选择适当的方法
// 示例中使用XMLHttpRequest发送POST请求
var xhr=new XMLHttpRequest();
xhr.open('POST', '/upload', true);
xhr.onload=function() {
if (xhr.status===200) {
// 上传成功
console.log('图片上传成功');
} else {
// 上传失败
console.log('图片上传失败');
}
};
xhr.send(formData);
}
}
API部分:
[HttpPost]
[RequestSizeLimit(5242880)]
public async Task<APIResult> upload(IFormCollection collection)
{
APIResult rtn=new APIResult();
if (collection==null)
{
rtn.code=-100;
rtn.msg="图片列表为空";
return rtn;
}
else
{
try
{
string file_path="";
// 预处理 用户参数:用户指定子路径
string userPath=DateTime.Now.ToString("yyyy-MM-dd");
if (collection.ContainsKey("path"))
{
collection.TryGetValue("path", out Microsoft.Extensions.Primitives.StringValues val);
if (!val.Equals("undefined"))
{
userPath=val.ToString();
}
}
// 预处理 文件路径
// 注意:这里可能会根据不同的环境来 修改 路径前面是否需要添加 /
// 当发现上传不成功,目录无法创建时,可以尝试修改这里
file_path=$"upload/imgs/{userPath}/";
var uploadPath=Path.Combine(_webHostEnvironment.WebRootPath, file_path);
if (!Directory.Exists(uploadPath))
{
Directory.CreateDirectory(uploadPath);
}
// 处理文件
FormFileCollection filelist=(FormFileCollection)collection.Files;
foreach (IFormFile file in filelist)
{
// 保存文件到磁盘
string name=file.FileName;
string FilePath=Path.Combine(uploadPath, name);
string type=Path.GetExtension(name);
using (var stream=System.IO.File.Create(FilePath))
{
await file.CopyToAsync(stream);
};
// 保存文件信息到表
Sys_File f=new Sys_File();
f.code="image";
f.name=name;
f.file_type=type.Trim('.');
f.file_group=userPath;
f.file_path=$"/{file_path}{name}";
f.is_active=true;
f.memo="";
f.createTime=DateTime.Now;
using (var dbctx=DBHelper.db)
{
await dbctx.AddAsync(f);
await dbctx.SaveChangesAsync();
};
// 返回消息,包含文件路径
rtn.datas=$"/{file_path}{name}";
rtn.code=100;
rtn.msg="文件已保存!";
}
}
catch (Exception ex)
{
rtn.code=-200;
rtn.msg="图片保存失败!";
Log4NetUnit.Instance.Log.Error("图片保存失败:" + ex.Message);
}
return rtn;
}
}
在这个示例中,我们首先在HTML中创建了一个<input type="file">元素,用于选择要上传的图片。
然后,我们在JavaScript中编写了一个uploadImage函数,该函数在点击"上传图片"按钮时触发。
在uploadImage函数中,我们首先获取到<input>元素,并从中获取到用户选择的图片文件。
然后,我们创建一个FormData对象,并将图片文件添加到其中。
接下来,我们可以使用XMLHttpRequest或fetch等方法将图片数据发送到服务器。
在示例中,我们使用XMLHttpRequest发送了一个POST请求,将图片数据作为FormData发送到/upload端点。
您需要根据您的实际情况修改URL和请求方法。
当请求完成时,我们可以根据响应的状态码来判断上传是否成功。
在示例中,如果状态码为200,则表示上传成功,否则表示上传失败。
请注意,由于安全性限制,JavaScript无法直接访问用户的文件系统。
因此,用户必须手动选择要上传的文件。
*请认真填写需求信息,我们会在24小时内与您取得联系。