MySQL 常用30种SQL查询语句优化方法

1、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

2、对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

3、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where num is null

可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:

select id from t where num=0

4、尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num=10 or num=20

可以这样查询:

select id from t where num=10

union all

select id from t where num=20

5、下面的查询也将导致全表扫描:(不能前置百分号)

select id from t where name like ‘%c%’

下面走索引

select id from t where name like ‘c%’

若要提高效率,可以考虑全文检索。

6、in 和 not in 也要慎用,否则会导致全表扫描,如:

select id from t where num in(1,2,3)

对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

7、如果在 where
子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然
而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

select id from t where num=@num

可以改为强制查询使用索引:

select id from t with(index(索引名)) where num=@num

8、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where num/2=100

应改为:

select id from t where num=100*2

9、应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where substring(name,1,3)=’abc’ –name以abc开头的id

select id from t where datediff(day,createdate,’2005-11-30′)=0
–’2005-11-30′生成的id

应改为:

select id from t where name like ‘abc%’

select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′

10、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

11、在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使
用,并且应尽可能的让字段顺序与索引顺序相一致。

12、不要写一些没有意义的查询,如需要生成一个空表结构:

select col1,col2 into #t from t where 1=0

这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:

create table #t(…)

13、很多时候用 exists 代替 in 是一个好的选择:

select num from a where num in(select num from b)

用下面的语句替换:

select num from a where exists(select 1 from b where num=a.num)

14、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段
sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

15、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或
update
时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数较好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。

16.应尽可能的避免更新 clustered 索引数据列,因为 clustered
索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered
索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

17、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会
逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

18、尽可能的使用 varchar/nvarchar 代替 char/nchar
,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

19、任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

20、尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

21、避免频繁创建和删除临时表,以减少系统表资源的消耗。

22、临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,较好使 用导出表。

23、在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log
,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

24、如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table
,这样可以避免系统表的较长时间锁定。

25、尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

26、使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

27、与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD
游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时
间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

28、在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF
。无需在执行存储过程和触发器的每个语句后向客户端发送 DONEINPROC 消息。

29、尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

30、尽量避免大事务操作,提高系统并发能力。

手机没联网,支付宝也支付成功了,原理是什么 ?

用支付宝时发现没开网络也能付款成功,原理是什么呢?有没有被破解的可能?

事实上支付宝和微信的“当面付”产品,是一款联机在线支付产品,所以不允许双方均离线的场景下支付(这一点和公交卡圈存支付不一样,公交卡的近场支付事实上允许双方脱机)。

题目中所知的支付宝和微信没有网络,指的都是消费者客户端没有网络,而不是双方都没有网络。

严格来说,当面付产品(特指商户主动扫消费者钱包客户端上的 token 码进行支付的形态)必须要商户在线方可进行联机交易,原因有以下两点:

  1. 支付公司为了保证资金安全必须要确保每笔用户的支付行为背后都真正发生了资金扣款,所以在线联机确保支付成功是必要的。(这里解释了为什么不允许双方脱机)

  2. 商户为了确保用户的支付结果可信赖,必须要自己的终端或者系统从支付公司获得支付结果,而不能以消费者的支付结果凭证作为结论。以传统 POS 业务举例,你可以认为你的刷卡信息等同于支付宝的当面付码,商户必须要看到 POS 机打出支付成功单据后才认为支付有效,如果 POS 支付超时没有回执,光凭客户手中的银行短信通知是不会让客户走的,而是会冲正掉上一笔交易让客户重新刷一笔。(这里解释了为什么要选择商户必须联机的方案)

那么,我们来看看一个标准的当面付产品的信息流是什么样的(原谅我草草画了一下):

我们可以看到在这个图里红色圈圈部分,商户系统和支付宝系统是对接上的,所以商户系统是联机的——而用户的手机,在展示 code
的时候,我并未强调是否和支付宝服务端联机。

事实上,不论是微信还是支付宝都支持两种用户码生成模式,即在线码和离线码。

在线码其实很容易理解,用户目前是登录钱包的状态,只要点击【付款】按钮,客户端就向支付宝的服务端申请一个针对这个客户账户的支付凭证码并展现到客户手机上。

这个支付凭证码在支付宝的服务端会有一组数据库记录其与真实客户账户之间的关联,并且这份关联的有效期为
60S,超过时效即便商户上送这个码,支付宝也会认为这是作废码而不予处理。

用户每次点开【付款】、等待超过 60S、主动刷新付款码,都会触发客户端向服务器申请一个新码的请求。

这个方案的好处是:

  • 相对安全,每次码都是服务端生成。

  • 业务灵活,即便对码的安全算法等进行较大的调整,也不用升级客户端,因为是服务端发码。

这个方案的坏处也显而易见:

  • 用户的手机客户端必须在线联网,如果没有网络则无法获取付款码

  • 用户即便在线,如果网络连接不好,也会出现点了付款等好久才看到码的情况,体验会不可控。

为了解决在线码方案的问题,离线码方案就出现了。

离线码的基本技术原理其实也不复杂,可以参考 @反方向的钟 的回答,比较简单的实现方式是:

用户登录后,服务端通过可信网络向用户客户端发送一个种子数据(每个客户的种子数据唯一,换用户登录后销毁原种子,重新下载种子)本地保存,当用户点击【付款】时,客户端利用这个种子数据

  • 时间戳 + 一套安全算法可以生成一串数字,即离线码。

当用户使用离线码支付时,服务端通过一定算法校验这个码的确是来自于这个用户,随即确认用户授权完成支付。

离线码的好处不言而喻:

  • 用户无需在线,就算在地下室等没有网络的场景一样可以使用

  • 由于不依赖网络,所有码本地生成,所以客户体验很好,一点付款码就能出来

那离线码的劣势呢,我们看看:

  • 用户 root/ 越狱手机后,保密存储的种子数据有可能被不法分子利用恶意程序获取到,导致离线码被随意生成用于消费。

恩……怎么说呢,毕竟现在不是发烧友主动 root 越狱的用户并不多,这是其一。

即便是 root 越狱,如果用户使用手机的习惯良好,被恶意程序攻击手机的概率也很低,这是其二。

每家公司都有自己的安全团队去保障自己客户端的数据安全,并不是说 root 的用户就只能坐以待毙了,否则微信和支付宝早被搞破产了,这是其三。

当然从我个人的角度来说,普通用户我都不建议去 root 或者越狱。

这个问题最粗暴的方案就跟反方向的钟所说的一样,监测到系统被 root 了就对用户限权(很多银行的客户端方案都是这么搞的)。

作为直接面向消费者市场且充分竞争的产品,微信支付和支付宝断然不会采用上面那个方案的。

怎么能又开放离线码给用户,又能确保用户支付安全,本身也是支付公司安全竞争力的一部分,这里就省略几万字了。

  • 数据碰撞可能导致 A 用户的码扣到 B 用户的账户

恩,这里涉及一些算法问题,业务上就是碰了巧了 A 用户码算出来和 B 用户码一模一样且都有效(两个客户端都没作弊)。

在线码之所以可以避免这个问题是因为在线码是服务端发的,可以控幂等。

离线码是客户端自己根据算法生成的,所以没法控。

其实原因和哈希算法的数据碰撞类似,是个小概率的纯技术问题,就不展开赘述了。

解决方案:优化算法(确保碰撞概率低到一定程度甚至杜绝),如果真的出现就认栽给客户赔钱(赔多了技术部门老大就肯定痛定思痛优化算法了)。

事实上这个问题发生的概率极低极低,所以可以忽略不计。

  • 算法调整不如在线码灵活

因为离线码生成逻辑都在客户端,所以通常来说安全算法升级会导致客户端升级,比在线码升级更影响用户一些。

分析到上面这层,各位产品经理相信应该就知道如何做方案选型了。(装个逼,事实上我觉得了解到上面那个层面是支付行业产品经理的基本素质)

后话:

我在写这个答案的时候其实都在刻意回避公司实现这些业务的具体逻辑和算法。而我个人并非当面付产品的产品经理,所以大家放心,这篇文章不算泄密。

写这个答案的目的是希望能尽量站在产品和业务角度还原业务原理,希望更多的非行业内的知友知其然,也知其所以然。

渐进式加载

#####
我们在PC端用浏览器看图片的时候,经常是先看到一张模糊图,然后再渐渐的变得清晰,这种情况在看漫画的时候尤其常见(模糊图如下),这种效果就叫做渐进式加载.渐进式加载能够大大的提升体验感,我们先来了解一下渐进式加载的原理.

(图片来自网络)

1.JPEG

要做到渐进式加载,我们的图片需要是JPEG格式,而JPEG格式的图片又分为两种,我们要做到渐进式加载的话,需要的是Progressive

JPEG.

(1)Baseline JPEG(标准型)

这种格式的图片在保存信息的时候,是从上往下,将每一行的数据顺序的保存起来的,所以读一部分就展示的话,那么效果就会像是从上往下一点一点展示.

(图片来自网络)

(2)Progressive JPEG(渐进式)

这种格式的图片在保存信息的时候,是一帧一帧的存储的,如果逐帧逐帧的读的话,就会先看到模糊图,然后一点一点变清晰

(图片来自网络)


(图片来自网络)

2.解码

如何判断是否JPEG格式的图片呢?下面引用一段Glide框架的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//ImageHeaderParser.java


private static final int EXIF_MAGIC_NUMBER = 0xFFD8;

// JPEG.


if (firstTwoBytes == EXIF_MAGIC_NUMBER) {


return JPEG;

}
我们可以看出,JPEG是以FFD8开头的
其实JPEG是以FFD8开头,FFD9结尾,FFDA代表一个帧的开头

FFD8 … FFDA … FFDA … FFDA … FFD9

1

Baseline JPEG 里面只有一个FFDA
Progressive JPEG 里面含有多个FFDA

比较完整的数据结构如下

(图片来自Wiki)

https://en.wikipedia.org/wiki/JPEG

3.如何保存或者转换成JPEG

(以下转换方法来自网络,由于非Java代码,所以没有做验证,特此说明一下)

1、PhotoShop

在photoshop中有“存储为web所用格式”,打开后选择“连续”就是渐进式JPEG。

据说,需要勾选那个转换为sRGB选项,在某些浏览器下,图像设置为CMYK会出现一些问题!

ps保存为png-24且勾选交错才能把图片保存为渐进式图片

测试效果如下:

2、Linux

检测是否为progressive jpeg : identify -verbose filename.jpg | grep Interlace(如果输出
None 说明不是progressive jpeg;如果输出 Plane 说明是 progressive jpeg。)

将basic jpeg转换成progressive jpeg:> convert infile.jpg -interlace Plane
outfile.jpg

3、PHP

使用imageinterlace和imagejpeg函数我们可以轻松解决转换问题。

1
2
3
4
5
6
7
8
9
10
11
<?php

$im = imagecreatefromjpeg('pic.jpg');

imageinterlace($im, 1);

imagejpeg($im, './php_interlaced.jpg', 100);

imagedestroy($im);

?>

4、Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import PIL


from exceptions import IOError


img = PIL.Image.open("c:\\\users\\\biaodianfu\\\pictures\\\in.jpg")


destination = "c:\\\users\\\biaodianfu\\\pictures\\\test.jpeg"

try:


img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)


except IOError:


PIL.ImageFile.MAXBLOCK = img.size[0] * img.size[1]


img.save(destination, "JPEG", quality=80, optimize=True, progressive=True)

5、jpegtran

1
jpegtran -copy none -progressive <inputfile><outputfile>

6、C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using (Image source = Image.FromFile(@"D:\temp\test2.jpg")) {


ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().First(c => c.MimeType
== "image/jpeg");


EncoderParameters parameters = new EncoderParameters(3);


parameters.Param[0] = new
EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);


parameters.Param[1] = new
EncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod,
(int)EncoderValue.ScanMethodInterlaced);


parameters.Param[2] = new
EncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod,
(int)EncoderValue.RenderProgressive);


source.Save(@"D:\temp\saved.jpg", codec, parameters);


}

7、C#转换

据说是如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using (Image source = Image.FromFile(@"D:\temp\test2.jpg")) {

ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().First(c => c.MimeType
== "image/jpeg");

EncoderParameters parameters = new EncoderParameters(3);

parameters.Param[0] = new
EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);

parameters.Param[1] = new
EncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod,
(int)EncoderValue.ScanMethodInterlaced);

parameters.Param[2] = new
EncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod,
(int)EncoderValue.RenderProgressive);

source.Save(@"D:\temp\saved.jpg", codec, parameters);

}

8、java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**

* 转换图片为 流式加载

* @author zhaosx

*

*/

public class ProgressiveJPEG {



    public static void main(String[] args) throws Exception {

        File file=new File("Z:/2.jpg");

        BufferedImage image = ImageIO.read(file);

        Iterator<ImageWriter> it =
ImageIO.getImageWritersByFormatName("jpeg");

        ImageWriter writer=null;

        while(it.hasNext()) {

             writer=it.next();

             break;

             //System.out.println(it.next());

        }

        if(writer!=null) {

             ImageWriteParam params = writer.getDefaultWriteParam();

             params.setProgressiveMode(ImageWriteParam.MODE_DEFAULT);

             //params.setCompressionQuality(0.8f);

             ImageOutputStream output = ImageIO.createImageOutputStream(new
File("Z:/22.jpg"));

             writer.setOutput(output);

             writer.write(null,new IIOImage(image,null,null), params);

             output.flush();

             writer.dispose();

             System.out.println("ok");

        }



    }



}

4.效果

明白了渐进式加载的原理后,我们就能想办法在app端也做到渐进式加载的效果了.

(大概就是判断是否JPEG图片,然后根据每一帧的节点来判断并决定是否需要加载)

下面展示一下效果图

(1)原图

(Progressive JPEG的图一打水印就变成Baseline JPEG,应该是CSDN打水印保存的时候处理了)

(2)解码到第一个FFDA与第二个FFDA的中间

(3)刚好解码到第二个FFDA

(4)解码到第五个FFDA

需要看图片二进制结构的,可以下载一些工具(如hex-editor-neo)

hex-editor-neo下载


图片的尺寸大小

同一张jpg图片,如果保存为基本式和渐进式那个尺寸更小呢?

根据我拿3终不同风格图片做测试,发现,百度百科中所说的渐进式图片的大小比基本式的小是不准确的。

其中,两者大小关系基本上没有什么规律。下面是我的测试结果:

图片品质59%及其以下时候,渐进JPEG图片更小;品质60%及其以上,基本JPEG更小
图片品质82%及其以上时候,渐进JPEG图片更小;品质81%及其以下,基本JPEG更小
无论图片品质多少,都是渐进JPEG图片更小

不过,个人臆测,从概率学上讲,大多数情况下,渐进式JPEG比基本式图片尺寸小一点。然而,其中的大小差异与原图尺寸相比,不值一提,因此,所谓图片大小不能作为两种图片选择的依据。

下载呈现速度

一个名叫Ann Robson的人,最近对各个浏览器下渐进式图片呈现做了测试。

下图为FireFox浏览器下呈现速度的对比图:

当大图轮廓加载OK的时候,小图最后一个乳猪还没有出世面;而基本式乳猪图还没有开始加载!显然,罗伯森是想告诉我们,渐进JPEG下载更快。

下表为其在各个浏览器下测试的结果:

浏览器 (特定测试版本) 渐进jpeg前景渲染 渐进jpeg背景渲染
Chrome (v 25.0.1323.1 dev Mac, 23.0.1271.97 m Win) 渐进地 (相当快!) 渐进地 (相当快!)
Firefox (v 15.0.1 Mac, 12.0 Win) 渐进地 (相当快!) 文件下载后立即地(慢)
Internet Explorer 8 文件下载后立即地(慢) 文件下载后立即地(慢)
Internet Explorer 9 渐进地 (相当快!) 文件下载后立即地(慢)
Safari (v 6.0 Desktop, v 6.0 Mobile) 文件下载后立即地(慢) 文件下载后立即地(慢)
Opera (v 11.60) 文件下载后立即地(慢) 文件下载后立即

结论很简单,Chrome + Firefox + IE9浏览器下,渐进式图片加载更快,而且是快很多,至于其他浏览器,与基本式图片的加载一致,至少不会拖后腿。

Scott Gilbertson对渐进式图片有其他的补充:

1. 你用永不知道基本式图片内容,除非他完全加载出来;

2. 渐进式图片一开始大小框架就定好,不会像基本式图片一样,由于尺寸未设定而造成回流——提高的渲染性能;

3. 渐进式图片也有不足,就是吃CPU吃内存。

内容就是这些,权衡使用在你手。一般而言,大尺寸图片建议使用渐进式JPEG.


另一种实现

图片在加载过程中由模糊到清晰的一个加载过程,需要两张图片实现,一张体积比较小,一张为原图,体积比较小的图片会先加载成功,然后对其进行模糊化处理,直到原图加载成功后,用原图替代体积较小的图。

css filter模糊处理:

html

1
2
3
4
5
6
7
8
9
10
<div class="placeholder" data-
large="<https://pic2.zhimg.com/50/v2-c5174d0b98facea9584e7766862decdd_400x224.jpg>">

    <img
src="<https://pic2.zhimg.com/50/v2-c5174d0b98facea9584e7766862decdd_60w.jpg>"
class="img-small">

    <div style="padding-bottom: 66.6%;"></div>

</div>

css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
.placeholder {

    width: 200px;

    background-color: #f6f6f6;

    background-size: cover;

    background-repeat: no-repeat;

    position: relative;

    overflow: hidden;

}



.placeholder img {

    position: absolute;

    opacity: 0;

    top: 0;

    left: 0;

    width: 100%;

    transition: opacity 1s linear;

}



.placeholder img.loaded {

    opacity: 1;

}



.img-small {

    filter: blur(50px);

    /* this is needed so Safari keeps sharp edges */

    transform: scale(1);

}

js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
window.onload = function () {



    var placeholder = document.querySelector('.placeholder'),

    small = placeholder.querySelector('.img-small')



    // 1: load small image and show it

    var img = new Image();

    img.src = small.src;

    img.onload = function () {

        small.classList.add('loaded');

    };



    // 2: load large image

    var imgLarge = new Image();

    imgLarge.src = placeholder.dataset.large;

    imgLarge.onload = function () {

        // small.classList.remove('loaded');

        imgLarge.classList.add('loaded');

    };

    placeholder.appendChild(imgLarge);

}

canvas模糊处理:

html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="figure">

    <div class="aspect-ratio-fill"></div>

    <img id="smallImg" class="small-img" crossorigin="anonymous"
src="<https://pic2.zhimg.com/50/v2-c5174d0b98facea9584e7766862decdd_60w.jpg>"

        alt="">

    <canvas id="canvas" class="canvas"></canvas>

    <img class="big-img" data-
src="<https://pic2.zhimg.com/50/v2-c5174d0b98facea9584e7766862decdd_400x224.jpg>">

</div>

css

.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
figure {

    position: relative;

    width: 200px;

    overflow: hidden;

}



.aspect-ratio-fill {

    padding-bottom: 66.67%;

}



.figure img,

.figure canvas {

    position: absolute;

    top: 0;

    left: 0;

    width: 100% !important;

    height: 100% !important;

    opacity: 1;

    transition: opacity 2s linear;

}



.canvas.loaded {

    opacity: 0;

}



.small-img {

    display: none;

}

js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let smallImg = document.querySelector('.small-img'),

    canvas = document.querySelector('.canvas'),

    bigImg = document.querySelector('.big-img');

ctx = canvas.getContext('2d');

smallImg.onload = function () {

    StackBlur.image(smallImg, canvas, 20, true);

    if (bigImg.getBoundingClientRect().top > 0 &&
bigImg.getBoundingClientRect().bottom < window.innerHeight) {

        bigImg.setAttribute('src', bigImg.dataset.src);

        bigImg.onload = function () {

            canvas.classList.add('loaded');

        }

    }

}

Jpeg渐进式图像数据解析

为了更好的优化客户端体验,客户端在图像压缩的时候采用了渐进式Jpeg压缩。渐进式Jpeg的好处是,只需要很少的一部分数据包,就能够解码出一副完整的图像,随着数据的增加,图像会不断变清晰。渐进式图像还有一个好处是每一处SOS的Huffman编码都是优化编码,平均图像size会小一些。

https://blog.csdn.net/APIX_CN/article/details/49780397?utm_source=blogxgwz26

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×