阅读视图

发现新文章,点击刷新页面。
🔲 ☆

HTML5 音乐可视化

下面梳理一下HTML5实现音乐可视化的流程。

搭建静态页面

静态页面的结构分三部分,header,left,right。header为语义化标签,left和right都用div来实现。

类似这样,然后把背景颜色改为黑色,字体改成白色,加上或不加边框线,给一定padding或margin,静态页面就搭建好啦。

不过重点不是页面布局。可以在左侧栏有一个曲目列表,点击切歌。这里就不那么麻烦,直接后台加载指定的歌曲。歌曲是许嵩的烟花笑,左侧栏显示部分歌词。

创建音频文件对象

AudioContext对象用于获取音频文件源。

(节点)AudioContext:包含各个AudioNode对象以及它们的联系的对象,可以理解为上下文对象。绝大多数情况下,一个document中只有一个AudioContext。

(方法)createBufferSource():创建audioBufferSourceNode对象。

可以这样写:

var ac = new window.AudioContext();    // 实例化ac为一个音频对象var bs = ac.createBufferSource();      // 用bs来保存音频文件资源

有了音频资源,是否就可以播放音频了呢?当然不可以,因为还没有指定文件,告诉对象播放哪一个音频文件。

获取音频数据

创建bufferSource后,到了AudioBufferSourceNode这个节点。这个节点有一个属性值buffer,用来指定文件资源。这个属性值需要一个buffer类型的数据。

(节点)AudioBufferSourceNode:表示内存中的一段音频资源,其音频数据存在于AudioBuffer中(其buffer属性)。

(属性)buffer:AudioBuffer对象,表示要播放的音频资源数据。

获取buffer有两种方式,一种是ajax设置返回值类型为arraybuffer,然后解码,一种是用FileReader读取文件,获得blob值。后一种方式多用input上传文件后获得文件,再进行解析。前面一种好用一点。(留坑)

ajax的原生写法:

var url="...";var xhr = new XMLHttpRequest();xhr.abort();xhr.open("GET", url);xhr.responseType = "arraybuffer";xhr.onload = function(){    return xhr.response;}xhr.send();

onload触发的函数中xhr.response就是我们想要的值。

(方法)decodeAudioData(arrayBuffer, succ(buffer), err) :异步解码包含在arrayBuffer中的音频数据。

将arraybuffer解码:

ac.decodeAudioData(arraybuffer, function(buffer){ ... },function(err){ ... });bs.buffer = buffer;    // 回调函数中的buffer就是想要的buffer

播放音乐

(方法)start:开始播放音频

这个时候调用start方法:

bs.start(0);

打开页面,音乐开始播放。

音量控制

(节点)GainNode:改变音频音量的对象,会改变通过它的音频数据所有的sample frame的信号强度。

(方法)createGain():创建GainNode对象。

(属性)value:可以改变音频信号的强弱,默认为1,最小值为0。

音乐播放还不行,要添加一个音量控制控件,音量控制用GainNode节点:

var gainNode = accreateGain();    // 创建GainNode对象gainNode.connect(ac.destination);    // 将gainNode连接到AudioDestinationNode节点

这样就有了一个音量控制的对象。

gainNode.gain.value= ... ;

gain.value用于控制输入信号的强弱,也就是音量的大小。HTML中用type为range的input,把值传递给对象,就可以实时控制音量了。

得到音频解析数据

(节点)AnalyserNode:音频分析对象,他能实时的分析音频资源的频域和时域信息,但不会对音频流做任何处理。

(方法)createAnalyser():创建AnalyserNode对象。

(属性)fftsize:设置FFT(是离散傅里叶变换的快速算法,用于将一个信号变换到频域)值的大小,用于分析得到频域,为32 ~ 2048之间2的整数次倍,默认为2048,实时得到的音频频域的数据个数为fftSize的一半。

(属性)frequencyBinCount,FFT值的一半,即实时得到的音频频域的数据个数

(方法)getByteFrequencyData(Uint8Array),复制音频当前的频域数据(数量是frequencyBinCount)到Uint8Array(8位无符号整型类型化数组)中。

先创建AnalyserNode对象,然后设置fftsize的值。FFT用于数字信号的处理,会把音频文件流输入的信号变换到频域,用getByteFrequencyData()方法得到经系统处理之后的频域数据。

var analyser = ac.createAnalyser();    // 实例化对象analyser.fftSize = 32;                 // 设置fftsizevar arr = new Uint8Array(analyser.frequencyBinCount);    // getByteFrequencyData()方法要求参数为8位无符号整型类型化数组analyser.getByteFrequencyData(arr);    //    arr为所需频域数据

这样得到的数组arr就是用于可视化效果绘制的数据。

(方法)window.requestAnimationFrame():告诉浏览器您希望执行动画,并请求浏览器调用指定的函数在下一次重绘之前更新动画。该方法将在重绘之前调用的回调作为参数。

一次解析只能得到一组数据,所以需要requestAnimationFrame不断解析,不断得到arr。

前端界面绘制

前端界面使用canvas绘制,实现音乐可视化的效果。

var box = document.getElementById("right");    // 获取right区域的dombox.appendChild(canvas);                       // 创建canvas节点var ctx = canvas.getContext("2d");             // 实例化canvas画笔

接下来就是使用for循环和ctx画矩形、圆圈,填充渐变的操作了。(留坑吧,估计不填了)

案例用到web Audio API的关系:

虚线是可以跳过的节点,直接播放音频文件。好吧有错。

🔲 ⭐

HTML select下拉框支持hr元素啦

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11503
本文可全文转载,独立域名个人网站无需授权,但需要保留原作者、出处以及文中链接,任何网站均可摘要聚合,商用请联系授权。

人手 铅笔 绘制 html hr 占位他图

一、hr元素与下拉分隔线

在过去,<select>下拉元素的子元素,只能是<option><optgroup>(下拉选项分组)元素。

而现在,<select>又多支持了一个元素,为<hr>元素。

可以在下拉选项中创建分隔线。

例如:

<select is="ui-select">
  <option>选项1.1</option>
  <option>选项1.2</option>
  <hr>
  <option>选项2.1</option>
  <option>选项2.2</option>
</select>

实时渲染效果如下:

下图是我电脑上的截图效果:

分隔线效果

我试试看这个hr样式能不能自定义。

结果,和option元素一样,是无法自定义的,sad 😔。

二、一些细节知识

首先,hr元素可以不闭合。

其次,如果<select>元素需要设置<hr>分隔效果,那么祖先元素不能是<p>元素,因为可能会有渲染问题。

例如下面这样的HTML代码:

<p>
  <select is="ui-select">
    <option>选项1.1</option>
    <option>选项1.2</option>
    <hr>
    <option>选项2.1</option>
    <option>选项2.2</option>
  </select>
</p>

在Chrome 129下,<hr>元素会中断<select>元素,变成下图所示的效果:

渲染异常

不过Chrome在版本131之后修复了这个渲染bug,不过用户不一定实时升级Chrome浏览器,因此,还是避免p > hr的标签组合。

兼容性

最先开始支持的是Chrome浏览器,时间应该是去年年底,caniuse上的截图:

select hr 兼容性

Safari支持最晚,今年3月份才开始支持。

为什么都是棕黄色,而非绿色呢,是因为可访问性这块还没怎么支持。

二、LuLu UI中的支持

LuLu UI组件的设计理念是基于原生HTML构建,既然浏览器原生支持了下拉框中的hr元素,LuLu UI 也定然要跟随支持。

所以,昨天在家,抽空弄了下,已经发版了。

演示页面地址访问:https://l-ui.com/edge/apis.select.html

分隔线 LuLu UI中的支持

Nice!

三、越来越短了

15年前刚写博客那会儿,恨不得每篇文章万把字,现在,年纪上去了,更新也还更新,但是字数缩写得厉害。

精力没有以前那么旺盛了,创作欲也下来了。

瞧瞧本文,之前篇幅的1/10.

不过嘛,现在的年轻人都喜欢短平快,说不定这种短短的内容他们更喜欢。

好了,不唠叨了。

这周三元旦,吼吼,天气也不错,可以出远门钓个鱼。

桀桀桀。

桀桀桀

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11503

(本篇完)

🔲 ⭐

记录HTML中与性能/渲染有关的几个新属性

记录 HTML 中与性能/渲染有关的几个新属性

<script>/<link>/<style>标签的blocking属性

mdn 对blocking 属性的解释:(实验性),这个属性明确指出,在获取脚本的过程中,某些操作应该被阻断。要阻断的操作必须是一个以空格分隔的列表,下面列出了阻断属性。

blocking属性允许我们能控制部分渲染的阻塞,直到特定的资源加载完毕。

使用

  • 可应用元素:<head> 标签中的 <script><link><style>
  • 值:"render"

设置blocking="render" 用于标记那些在显示任何内容给用户之前必须加载完成的资源。在这些资源加载完成之前,浏览器窗口中不会绘制任何像素。

<script>使用

如:

1
<script blocking="render" src="main.js" defer></script>

默认情况下,浏览器会为所有渲染阻塞的资源分配较高的优先级。但考虑到不是所有浏览器都支持 blocking 属性,添加 fetchpriority 属性设置 fetchpriority="high" 也是个不错的选择:

1
<script blocking="render" fetchpriority="high" src="main.js" defer></script>
内联脚本

blocking 属性同样适用于内联脚本。传统的内联脚本默认是阻塞的(deferasync 属性不适用于它们)。但是,如果你的脚本带有 type="module" 属性,即使是内联的,它也会延迟执行。

1
2
3
<script type="module" async blocking="render">
// 重要的 JavaScript 代码...
</script>

内联模块脚本中的 async 属性意味着它会尽快执行。

<link>使用

1
<link rel="stylesheet" href="main.css" blocking="render" />

可以在 <link> 元素上设置 blocking 属性。但如果你使用 <link> 配合 rel="preload" 来预加载资源,或者使用 rel="modulepreload" 来预加载 JavaScript 模块,那么 blocking 属性将不会生效。

开发者通常会利用这种行为来有意识地延迟非关键 CSS 的加载。但是,如果你需要以这种方式加载关键 CSS(会改变整个页面布局或影响页面折叠内容的样式),则可以使用 blocking 属性:

1
2
3
4
5
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = './styles.css';
link.setAttribute('blocking', 'render');
document.head.appendChild(link);

渲染阻塞 DOM 节点

HTML 的渲染是逐步/增量进行的:渲染可以在整个 HTML 文档被获取和解析之前就开始。

有一种方法可以根据特定 HTML 元素是否已解析来阻塞渲染。

<head> 中放置一个 <link>,其 href 属性引用所要元素的 id

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<link rel="expect" href="#visually-critical-content" blocking="render" />
</head>
<body>
<header>...</header>
<div id="visually-critical-content">...</div>
</body>
</html>

这使得开发者可以更好地控制哪些内容包含在首次内容绘制 (FCP) 中。它应该只用于页面折叠之上的元素——否则你会不必要地延迟首次绘制。一旦元素被解析,页面就会对用户可见。如果找不到预期的元素,则在整个 HTML 文档解析完成时,渲染将被解除阻塞。

这个功能主要是在考虑跨文档视图转换的情况下添加到 Web 中的。

renderBlockingStatus 检测是否生效

如果你想验证 blocking 属性是否生效,可以在浏览器的开发者工具控制台中运行以下代码,来查看渲染阻塞资源的列表:

1
2
3
4
window.performance
.getEntriesByType('resource')
.filter(resource => resource.renderBlockingStatus === 'blocking')
.forEach(resource => console.log(resource.name));

那我们为什么需要这个新属性呢?

它明确了阻塞的行为,清晰地传达了开发者的意图,这样团队成员在重构代码时,就不会无意中把它改成非阻塞的了。
当与 deferasync 属性一起使用时,它可以阻塞渲染,同时不会阻塞 HTML 解析器的工作。
与传统脚本不同,模块脚本默认是延迟加载的。<script type="module"> 现在可以通过 blocking 属性来实现渲染阻塞。
通过 JavaScript 动态添加到 <head> 中的 <script><link><style> 元素,默认是不会阻塞渲染的。但现在,你可以灵活地让它们实现阻塞。

是否可能会延长 FCP?

渲染阻塞会延迟将像素绘制到屏幕上,所以你可能会想,这是否意味着用户就得盯着空白页面看。在快速的网络连接下,这种情况很少见。过去,在浏览网页时,经常会在页面间看到白色的闪烁。为了解决这个问题,浏览器开始使用绘制保持(paint holding)。绘制保持会让用户停留在上一个页面,并显示一个加载指示器,直到新页面的首次内容绘制 (FCP) 准备就绪。如果你延迟了 FCP,用户就会在上一个页面上多停留一会儿。绘制保持只持续很短时间。如果你延迟了 FCP 太久,就会显示一个空白的白色页面。对于速度慢的网站,在慢速的 3G 网络上,这种白色空白的“闪烁”可能会持续很长时间。

兼容情况

p-blocking


<img>/<iframe>标签的loading属性

mdn 对于图片loading 属性的解释:HTMLImageElement 的 loading 属性为一个字符串,它的值会提示 用户代理 告诉浏览器不在可视视口内的图片该如何加载。这样一来,通过推迟图片加载仅让其在需要的时候加载而非页面初始载入时立刻加载,优化了页面的载入。

可以控制资源的加载行为,减少初始页面加载时间。

使用

设置值为"eager"时(默认行为),eager 告诉浏览器当处理 <img> 标签时立即加载图片;设置值为lazy时告诉用户代理推迟图片加载直到浏览器认为其需要立即加载时才去加载。例如,如果用户正在往下滚动页面,值为 lazy 会导致图片仅在马上要出现在 可视视口中时开始加载。

load 事件的时机

<img>

load 事件在文档被完整的处理完成时触发。当图片使用立即加载 (默认值) 时,文档中的所有图片都会在 load 事件触发前载入。

当 loading 值设为 lazy 时,图片不再会在请求,下载,处理的时间内推迟 load 事件触发。

loading 属性值设为 lazy 但是在页面初次加载时就在可视视口内的图片会立即加载但它们也不会推迟 load 事件。换句话说,这些图片不会在处理 元素时立即加载,但仍会作为页面初始加载的一部分而加载。他们只是不会影响 load 事件。

这表明当 load 触发时,可视区域内懒加载的图片可能不可见。

<iframe>

即使 iframe 位于可视视口并在页面加载时被请求,懒加载的 iframe 也不会影响 load 事件的时机。只有当文档中所有立即加载的 iframe 都被请求后,才能触发 load 事件。

<img>使用

1
<img src="image.jpg" loading="lazy" />
防止元素在图片懒加载时出现移位

当一个加载被 loading 属性设为 lazy 的图片最后加载时,浏览器会根据<img> 元素的尺寸和图片自身大小重排文档,更新被图片影响的元素的位置。

为了防止重排发生,你需要使用 widthheight 属性明确设置图片大小。通过这样建立固有长宽比,你防止了元素的移位。取决于实际的加载时间和重排,移位造成的最小的影响可能只是使用户困惑和不适,最坏的影响则是导致用户点错目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function addImageToList(url) {
const list = document.querySelector('div.photo-list');

let newItem = document.createElement('div');
newItem.className = 'photo-item';

let newImg = document.createElement('img');
newImg.loading = 'lazy';
newImg.width = 320;
newImg.height = 240;
newImg.src = url;

newItem.appendChild(newImg);
list.appendChild(newItem);
}

iframe 使用

1
<iframe src="example.html" loading="lazy"></iframe>

以下示例展示了如何定义一个懒加载的 iframe,然后将其附加到文档中的 <div>。只有当 iframe 即将变得可见时,才会加载 iframe

1
2
3
4
5
6
7
8
9
10
// 在 iframe 中定义懒加载
const iframe = document.createElement('iframe');
iframe.src = 'https://example.com';
iframe.width = 320;
iframe.height = 240;
iframe.loading = 'lazy';

// 添加到类名为 frameDiv 的 div 元素中
const frameDiv = document.querySelector('div.frameDiv');
frameDiv.appendChild(iframe);

兼容情况

p-loading


<a>/<link> 标签的 hreflang 属性

mdn 对hreflang 属性的解释:此属性指明了被链接资源的语言。其意义仅供参考。可取的值参见 RFC 5646: 语言识别标签(又称 BCP 47)。仅当设置了 href 属性时才应设置该属性。

hreflang属性指定<a><link>元素上链接资源的语言。它的工作原理类似于lang 属性,但专门用于链接。

您可以通过在内部和外部网站链接中使用 hreflang 来改善用户体验和 SEO。在内部网站链接上使用 hreflang 提供了一种方法,可以告诉搜索引擎其他语言或地区的页面的不同变化。这意味着讲英语的人将收到该网站的英文版,而讲瑞典语的人将收到瑞典文版。用户端无需手动切换,体验更加流畅。

使用

将带有所需 ISO 语言代码的 hreflang 属性添加到<a>元素中。对于英文网站则是 en

1
<a href="https://example.com" hreflang="en">English Website</a>

您也可以使用更具体的语言代码,并使用区域变化。例如我们可以为英式英语添加 en-GB

1
<a href="https://example.at" hreflang="en-GB">English Website</a>

如果您的网站提供多种语言版本,您可以使用 hreflang 指定与特定 URL 关联的文档的语言和区域。添加该属性将有助于搜索引擎了解不同网页版本的语言和区域定位。

在每个链接中添加带有所需语言代码的 hreflang。一个链接应该作为默认的后备版本,通过添加 hreflang="x-default"而不是语言代码来识别。最后对于语言切换器中的每个链接,将 rel 属性的值设置为"alternate",以指示所链接的页面是当前页面的替代页面。

1
2
<link href="https://example.com" rel="alternate" hreflang="x-default" />
<link href="https://example.com/de" rel="alternate" hreflang="de" />

您也可以在语言切换器中使用 hreflang

1
2
<a href="https://example.com" hreflang="x-default">English</a>
<a href="https://example.com/de" hreflang="de">German</a>

有时语言切换者使用他们切换到的语言中的链接文本。您可以通过额外使用 lang 属性来指示这一点。

1
2
<a href="https://example.com" hreflang="x-default">English</a>
<a href="https://example.com/de" hreflang="de" lang="de">Deutsch</a>

增强可访问性的另一种方法是将 aria-current="true"包含到当前活动的链接中。

1
2
<a href="https://example.com" hreflang="x-default" aria-current="true">English</a>
<a href="https://example.com/de" hreflang="de" lang="de">Deutsch</a>

兼容情况

p-blocking


通用属性translate

mdn 对translate 属性的解释:全局属性 translate 是一种枚举属性,用来规定对应元素的可翻译属性值及其 Text 子节点内容是否跟随系统语言作出对应的翻译变化。

translate 属性用于指示元素是否应该被翻译。

为什么使用 translate?

默认情况下,大多数网站文本都是可翻译的(除了一些例外,例如图像上或 SVG 中的文本)。如果网站的定义语言与浏览器的默认语言不同,翻译工具(如 Google 翻译)可能会建议翻译页面内容。

但可能存在这种行为是不需要的情况。公司名称、电子邮件地址或代码示例等特定术语通常不应翻译,以避免混淆。自动翻译并不总是完全准确的,特别是对于小众词汇或技术词汇。

使用

您可以在任何 HTML 元素上使用 translate。指定一个空字符串("")或 yes 用于翻译,而 no 用于避免翻译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 德语原文 -->
<p>
<span>Wien<span>
ist (wieder) die lebenswerteste Stadt der Welt!
</p>
<p>
<span translate="no">Wien<span>
ist (wieder) die lebenswerteste Stadt der Welt!
</p>

<!-- 翻译后 -->
<p>
<span>Vienna<span>
named world's most liveable city (again)!
</p>
<p>
<span translate="no">Wien<span>
named world's most liveable city (again)!
</p>

兼容情况

p-translate.png


相关链接

🔲 ⭐

一种新型蠕虫:花瓣CORSBOT蠕虫

新年新气象,这个蠕虫我做了小范围测试,也提交了官方修复,小圈子里做了分享,这里正式对外公布下,出于研究而非破坏为目的,供大家参考。

IAMANEWBOTNAMEDCORSBOT
——-BOT PoC——-
http://evilcos.me/lab/IAMANEWBOTNAMEDCORSBOT.TXT

完整代码直接见上面这个链接即可。

里面需要特别注意的两个点:

  1. Conten-Type是JSON
  2. Origin是:huaban.com.al3rt.io,这个小技巧直接绕过花瓣的Origin判断

所以,蠕虫得以传播。

本质是:CORS(Cross-Origin Resource Sharing)的滥用导致,效果图:

最后,友情提示下:这个问题还是比较普遍的。脑洞怎么开,玩起来才会知道,不玩永远想不到:-)

🔲 ☆

关于浏览器混合内容的安全性

Seebug Paper之前收录了三篇文章有些关联性,分别是:

  1. 绕过混合内容警告 – 在安全的页面加载不安全的内
    http://paper.seebug.org/112/
  2. 检测本地文件躲避安全分析
    http://paper.seebug.org/87/
  3. 基于浏览器的指纹识别:影响和缓解措施
    http://paper.seebug.org/64/

有个关键点是:混合内容的安全性

这里提到的主要是协议的混合,如https/http/file/res/mhtml等,现代浏览器逐渐开始隔离这些协议,比如https下加载http的内容时,给出安全提示;再比如“修补”file/res/mhtml这些协议在http协议的网页里带来的安全问题,如本地文件探测(常见的是杀软探测)。

玩前端Hack的都知道,修补与绕过总是在博弈,而且浏览器们的修补是存在差异的,也就是可能出现浏览器安全差异。

很早以前,我在写https协议下的XSS exp时,就注意到:当载入http内容时,浏览器的安全提示。后来解决方案是采用DOM动态操作来绕过,这个技巧和第一篇文章里提的一样。为什么这样可以?这个关键点就是浏览器安全机制的触发机制,采用DOM动态操作时,这个浏览器安全机制并不会触发。我的理解是DOM的操作实在过于繁杂,为了性能平衡,许多浏览器安全机制并不会触发,而仅在页面加载时触发。当然,我也认为这种理解不总是成立,具体问题还得看场景分析。

这种特性能带来很多有意思的前端Hack技巧,比如AngularJS发布时,我就做了个有趣的安全测试(Bypass CSP),当时利用AngularJS丰富的前端异步加载机制,有些外域内容通过合法的异步加载再在本页面渲染解析,达到无视CSP策略的效果。

顺着这个关键思路,我们的绕过就不需要那么直接去面对对应的安全机制,而是采用规避方式:也就是想办法不触发某些我们不喜欢的安全机制,这是我们绕过的核心点。

还有,第二篇文章提到的window.open技巧,是一种好技巧,浏览器需要特别对待,否则这对于前端Hacker来说也是个非常好的“触发机制混沌区”。在我看来,浏览器对待window.open多少有些无奈,首先是历史原因导致还未摒弃,然后奇特的父子权限模型,及为了用户体验而允许点击事件下的不拦截弹窗,这还导致了一系列其它类别的安全问题,如钓鱼。

回到混合内容的安全性。现在及未来,不仅协议之间如此隔离,内容也一样,CSP策略正是为此而生。

在HTML5如日中天及前端框架百花齐放的现在,DOM里的博弈不会消减,可预见的是会更加激烈。虽然,这三篇文章都是上古时代的Hack延续了…

本文,手机上写的。先这样:-)

❌