2026-11-06 17:51:24
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12481
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
关于拖拽的文章,我之前写过至少两三篇,之前实现逻辑都是这样。
一个拖拽标志量,一个存储数据的变量。
mousedown后标志量开启,存储初始数据,
然后在document文档上绑定mousemove,实时计算位置。
等mouseup后再恢复和处理。
用了很多年,自我感觉也挺好的,眼睛闭着都能手写出来。
实际上,上面这套实现已经过时了,且过时很久了。
现在还在使用mousedown实现拖拽效果,那就是个棒槌了。
应该使用pointerdown、pointerup这类事件(PC、Mobile双通),同时,移动的事件也不需要绑定在document或者<body>元素上了,直接写在触发元素上即可!
有人会问了,要是拖动太快,鼠标从触发元素上移出,岂不是pointermove事件不会触发?
是有这个问题,不过,此事件在设计的时候就考虑到这种场景了,于是设计了setPointerCapture方法,专门针对此问题。
setPointerCapture可以捕获事件的pointerId,在鼠标或手势释放之前,后续所有事件都将以该捕获元素为目标。
所以拖拽的时候,不要担心目标元素会丢失。
眼见为实,直接看案例,假设一个球是ball:
<div class="ball" id="ball"></div>
那么所有拖拽事件都可以绑定在 ball 元素上,通过setPointerCapture捕获,不要担心拖拽行为那么容易丢失。
JS代码参考示意:
let dragging = false;
let offsetX = 0;
let offsetY = 0;
ball.addEventListener("pointerdown", function (event) {
dragging = true;
// 最关键代码就是我
ball.setPointerCapture(event.pointerId);
...
});
ball.addEventListener("pointermove", function (event) {
if (dragging == false) {
return;
}
...
});
const stopDrag = function (event) {
dragging = false;
if (ball.hasPointerCapture(event.pointerId)) {
ball.releasePointerCapture(event.pointerId);
}
};
ball.addEventListener("pointerup", stopDrag);
ball.addEventListener("pointercancel", stopDrag);
ball.addEventListener("lostpointercapture", stopDrag);
鼠标或手指按下的时候setPointerCapture,抬起的时候释放releasePointerCapture,就可以完成体验流畅的拖拽效果啦。
眼见为实,您可以狠狠地点击这里:setPointerCapture实现小球拖拽demo

演示效果是AI生成的,虽然不是我的UI美学风格,但是算了,省心啊,嘿嘿😋。
哎呀,内容有点少了,再水些内容吧。
说到Pointer,我不自觉又联想到了requestPointerLock,这个是用来实现无限滚动的,属于JS Pointer Lock的内容,之前写文章介绍过:鼠标无限移动 JS API Pointer Lock简介
CSS的pointer-events:none可以干掉JS pointer相关的事件,不过却阻止不了focus交互。
还有啥,好有啥,我想想……
哦,还有个touch-action,与触摸有关的,可以阻止滚动啥的,可以防止自怼拖拽和原生滚动冲突。
然后老的setCapture()方法已经废弃了,万不可使用。
噢啦,差不多就这么多了。
感谢大家的阅读,周末愉快,祝我爆护。
这是我上周末钓的,168五小时,钓得都铺出来了,😁😁。

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12481
(本篇完)
2026-10-30 19:47:48
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12455
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
一句话:sizes="auto" 是给懒加载图片用的新特性,浏览器不再需要你手写媒体查询,会自动读取图片布局后的实际渲染宽度,去 srcset 里挑选最合适尺寸的图片。
关于srcset属性,我在之前的文章(响应式图片srcset全新释义sizes属性w描述符),以及我写的《HTML并不简单》这本书里面都有过详细的介绍。
比方说下面这段HTML代码:
<img src="128px.jpg" srcset="128px.jpg 128w, 256px.jpg 256w, 512px.jpg 512w" sizes="(max-width: 480px) 100vw, 480px">
我问你,iPhone6下加载的是哪张图片?渲染的宽度是多少?
有谁回答的上来!
需要知道iPhone6的屏幕密度,需要知道他的尺寸,然后计算使用哪个规格的图片。
麻麻批的,我自己都看不懂,虽然这段代码是我自己写的。
这就是为何,从业十几年,我从来没有在哪个国内的项目见谁用过srcset和sizes属性。
太复杂,太啰嗦,所以,大家都是直接2倍图,3倍图一把梭,反正用的是公司流量。
现在情况有变,在某些条件下,开发者无需去关注sizes的media查询细节了,直接sizes="auto"就可以了,浏览器会自动选择合适尺寸的图片去加载。
那这个条件是什么呢?为什么浏览器可以自动选择尺寸?
这个条件就是图片需要设置loading="lazy"。
设置了懒加载的图片默认不是立即加载的,对吧,需要图片元素滚动到视口,或者接近视口的时候(与网速、屏幕尺寸等有关)才开始加载。
此时页面的 CSS 早就解析完毕,图片在布局中的准确宽度浏览器早就知道了。
既然尺寸都知道了,那么不就可以后自动匹配对应的srcset资源了?
这就是为何sizes="auto"需要图片是懒加载的原因。
补充说明
图片除了必须是懒加载的,还需要显式指定 width 和 height(或通过 CSS 限制宽高),或者指定一侧高度,然后使用aspect-ratio属性约束好比例。
<img sizes="auto" loading="lazy">还是个新特性,我看了下,Safari居然还没有支持:

唉,无所谓了,不支持就不支持吧,反正不影响图片正常显示,到时候src随便给个两倍图就好了。
至于srcset属性的使用窍门那就简单了,原图宽度是多少像素,后面就写多少w,比方说:
<img loading="lazy" sizes="auto" srcset="img-300.jpg 300w, img-600.jpg 600w, img-1200.jpg 1200w" src="img-600.jpg" width="600" height="400" />
当然,省流量还有其他更好的方式,比方说jpg格式换成webp,如果条件允许,最好换成avif格式,这个才是兼顾视觉效果和文件大小的王牌格式,同画质下,比webp小20%~30%的体积,和jpg格式相比,更是减半不止。
目前所有主流浏览器都支持avif格式了,大家有兴趣可以多多关注。
比研究srcset这样细节的知识对产品流量的增益大多了。
当然,本文的内容并不是一无是处,反正现在有AI,回头开发的时候,让AI自动生成多尺寸图片(以前多半要手动处理),使用srcset属性调用,也可以。
毕竟以前实现类似东西要烧脑子,现在只要烧token,开发成本不可同日而语。
好了,就说这么多。
顺便汇报下之前说的在线前端课程,已经写到章节2.5了,阅读约莫20~30%的样子吧。
再写几个小节,就可以找编辑看看能不能签约了,谈分成之类的。
股票跌没了,得找点路子赚点零花钱补贴家用了。

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12455
(本篇完)
2026-10-26 15:09:37
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12450
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
今天介绍一个特殊的@supports检测函数,那就是named-feature(),目前就下面两个经典用法:
/* 检测:锚点定位是否感知 transform(transform后锚点位置跟着变) */
@supports named-feature(anchor-position-follows-transforms) {
/* 支持该行为时,启用高级锚点布局 */
.tooltip {
position: anchor(--btn);
}
}
/* 检测单轴滚动容器能力 */
@supports named-feature(single-axis-scroll-container) {
.scroll-x {
overflow-x: auto;
overflow-y: clip;
}
}
有别于之前的(property: value)属性检测、selector(:has())选择器检测、at-rule(@container) AT规则检测。
这里的named-feature()检测的函数参数是CSS规范里显式登记的特殊名字。
先说anchor-position-follows-transforms。
锚点定位 anchor() 有一个大坑,那就是部分旧版实现,元素做 transform 之后,锚点是坐标不会跟着更新的。
锚点定位是非常强悍的CSS特性,很好用,好用的不得了,有兴趣可以访问我之前的文章:全新的CSS Anchor Positioning锚点定位API
如果使用传统语法检测,比方说@supports (position: anchor()),结果只能检测浏览器认识 anchor 语法与否,并不能检测transform定位bug浏览器是否修复。
所以,就必须用 named-feature(anchor-position-follows-transforms) 判断行为是否符合预期,以此区分两套实现逻辑,避免 UI 错位。
说起单轴滚动,就不得不提我上个月写的这篇文章:一个视频彻底看懂CSS scroll-axis-lock none的作用
虽然都是轴滚动相关的特性,但这里的检测背景和作用与之还有所不同,这个值得展开说说。
比方说设置一个表格可以水平和垂直滚动,结构如下:
<div class="table-wrapper" style="overflow-x: auto;">
<table>
…
</table>
</div>
然后希望表头和第一列粘性定位:
.table-wrapper thead {
position: sticky;
top: 0;
}
/* 第一列也粘性定位*/
.table-wrapper td:first-child {
position: sticky;
left: 0;
}
结果怎么着,水平滚动是有粘性定位效果的,但是垂直滚动就没有。
为何?
这是一个“次轴无法被彻底裁剪”历史遗留问题。
长期以来,CSS overflow 的组合存在限制。
如果你设置了 overflow-x: auto,哪怕将 overflow-y 显式设为 clip 或 hidden,浏览器在底层计算滚动上下文时,仍然会将该容器视为两个轴向都能滚动的容器(或者说,计算逻辑仍会将其视为 overflow-y 产生了滚动边界)。
插播一下,如果不知道overflow:clip的作用,可以看我很多年前写的这篇文章:CSS overflow-clip-margin属性简介
于是乎,大表格的粘性定位就成了受害者。
浏览器会误认为该外层容器在 Y 轴也是滚动容器,从而导致表头被困在外层容器中,无法跟随页面垂直滚动条实现粘性固定(这句话不理解也没关系,AI时代了,细节原理不重要了)。
效果见下面录屏示意(不动点击播放),左侧是有问题的,右侧是目标效果,左侧可以看到本该粘性定位的表格头直接被滚走了。
停顿了片刻……
算了,还是有不少好学的小伙伴的,上面这句话再通俗解释下吧。
就是我特么的只设置了水平滚动,垂直方向我压根没有任何滚动,你不能傻乎乎的看到我是个滚动容器,就不让我垂直方向的表头无法粘性定位吧!
PS:粘性定位的启动点计算是根据第一个滚动容器来的,详见此文:深入理解position sticky粘性定位的计算规则
为了解决该问题,CSS 规范进行了修订,允许 overflow-x: auto 与 overflow-y: clip(或通过简写 overflow: auto clip)配对时,容器仅充当单轴(X轴)滚动容器。
此时,Y 轴的 position: sticky 就可以绕过该容器,正确地去跟踪文档(页面)的垂直滚动条。
根据我从chrome开发者官方页面看到的数据,貌似是Chrome 154开始支持的。
而为了检测浏览器是否开始了单轴滚动的支持,就需要用到这里的named-feature(single-axis-scroll-container)检测。
为什么不能直接用 @supports (overflow: auto clip)?
@supports (overflow: auto clip),浏览器只会检查自己能不能解析这个语法。所以这里才需要@supports named-feature(single-axis-scroll-container)检测浏览器是否已经支持了overflow:clip与浏览器单轴滚动。
如果不支持,我们可以使用JS打个补丁,或使用其他策略修复。
对了,演示页面差点忘记了,如果你的浏览器Chrome版本154及其以上,可以狠狠地点击这里:CSS单轴滚动容器的怪异行为以及修复demo
named-feature()特征检测其实没什么好讲的,因为太小众了,我估计全中国,99.9%的前端都没有机会在实际项目中使用此特性。
那为什么还专门写一篇文章呢?
主要是这两个特征检测背后的故事很有意思,值得念叨。
一般named-feature()特征检测都是检测浏览器之前有某某渲染问题,在之后的版本中修改了策略,或者修复了bug,然后针对这个现象,专门弄个白名单机制一个的特殊名称。
方便开发者更好地对新老特性做兼容。
很好的设计想法。
但是太小众,AI时代的前端开发者普遍没什么学习热情,怎么可能学这种东西,狗都不学。
接下来看下此特性的兼容性,看图:

哇,好新的特性啊。

得,更是没什么学习价值了。
😮💨😮💨😮💨
好,就说这多,我们下周再见!

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12450
(本篇完)
2026-10-14 18:24:41
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12450
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
3年前有介绍过内联元素的balance排版,就是使用text-wrap:balance声明,很好用,我已经在真实项目中使用了。
可以让居中排版的多行元素每一行的数量尽可能均衡,使视觉效果更好。

眼见为实,您可以狠狠地点击这里:text-wrap:balance实现头像均衡布局demo
目前所有主流浏览器都已经支持,可以放心大胆使用,就算浏览器不支持,也仅仅是传统排版效果而已。

更多信息可以访问这里:快速了解text-wrap:balance的作用
好消息!
现在不仅内联元素,块级元素也支持balance排版了,那就是flex-wrap: balance。
Flex布局实现balance排版很简单,只需要将传统的flex-wrap:wrap声明替换为flex-wrap:balance就可以了。
来看下效果对比,默认wrap排版会优先前排的宽度,直到宽度放不小,才会换行显示,比方说这般:

如何选择balance排版,则每一行的标签数量会尽可能的均等,于是排版布局效果就会是这样的:

眼见为实,您可以狠狠地点击这里:flex-wrap:balance与Flex布局均衡排版demo
默认情况下的均衡排版会充分利用容器宽度。
实际上,Flex布局提供了能力,可以指定均衡排版的行数,就是使用flex-line-count属性。
例如:
.container {
display: flex;
flex-wrap: balance;
flex-line-count: 3;
}
那么书籍标签就会三行显示,每行均分两个标签,效果如下图所示:

衍生作用
指定flex-line-count的数量后,我们其实可以很方便的在Flex布局中实现瀑布流布局效果。
指定Flex容器为垂直排版,设置flex-line-count为固定的列表值,此时,无论Flex子项的数量多少,都会瀑布一样的方式往下排版。
大家不妨想想一下上面三行均衡排版图旋转90°之后的效果,如果列表项足够多,是不是就是瀑布流排版呢?
具体示例我就不放出来了,AI时代,大家知道有这么回事就好了。
或者看下面的示意脑补下吧,

Flex布局的balance排版的兼容性如下:

虽然看起来Chrome 150才支持,但并不影响我们的使用,因为某种意义上,此特性是渐进增强特性。
.container {
display: flex;
flex-wrap: wrap;
flex-wrap: balance;
}
如果浏览器支持,那就是balance排版,如果不支持,还保持现在的wrap排版,并不会造成显著的影响。
碎碎念时间
最近突然冒出了个step-5的模型,最近CodeBuddy和WorkBuddy中限免,试用了下,除了思考慢了点,最终效果还可以。
是一家名叫阶跃星辰 – stepFun的公司开发的,以前听都没听过,突然冒出来的,还真是有趣。
说不定什么时候,某个会自学习的AI模型是从其他模型公司突然涌现。

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=12450
(本篇完)
2026-10-08 18:38:55
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=12447
本文可全文转载,但需要保留原作者、出处以及文中链接,AI抓取保留原文地址,任何网站均可摘要聚合,商用请联系授权。
画中画的实现这个2018年的时候就有介绍,乖乖,已经是8年前了,不过那个时候只支持视频元素,可以访问这里了解:video视频播放Picture-in-Picture画中画技术
现在,不仅是视频元素,其他普通元素现在也支持画中画的能力了,就是使用documentPictureInPicture.requestWindow()方法。
在AI时代,我们无需关心具体的API细节。
因此,我不打算介绍各种方法参数,直接上案例。
您可以狠狠地点击这里:普通元素画中画呈现效果demo
点击如下图所示的按钮,就可以看到图片元素的小窗口在显示器的右下方了,即使浏览器最小化,此窗口依然存在。


之所以进入画中画状态后,背景色从深色变成浅色,是因为加入了@media (display-mode: picture-in-picture)的测试代码。
如下所示:
figure {
background-color: #333;
color: #fff;
}
@media (display-mode: picture-in-picture) {
figure {
background-color: #f5f5f5;
color: #333;
}
}
先看下HTML结构,一个提示元素,一个主体展示元素:
<p id="inPicMsg" hidden>图片信息元素处于画中画状态。</p>
<figure>
<img src="hanyun.jpg" alt="张含韵">
<figcaption>画面中这人是谁?</figcaption>
</figure>
<button id="inPicBtn" type="primary" class="ui-button">进入画中画</button>
关键还是交互部分,如下所示:
const figure = document.querySelector("figure");
// 进入按钮和进入提示元素
const inPicBtn = document.getElementById("inPicBtn");
const inPicMsg = document.getElementById("inPicMsg");
inPicBtn.addEventListener("click", function () {
window.documentPictureInPicture.requestWindow({
width: figure.clientWidth + 32,
height: figure.clientHeight + 72,
}).then(pipWindow => {
// 添加关闭处理
pipWindow.addEventListener("pagehide", (event) => {
inPicMsg.hidden = true;
inPicMsg.after(figure);
});
// 显示提示
inPicMsg.hidden = false;
// 创建渲染样式
const style = pipWindow.document.createElement("style");
fetch(document.querySelector("link[href^='http']").href)
.then(response => response.text())
.then(text => {
style.textContent = text;
});
pipWindow.document.head.append(style, document.querySelector("style").cloneNode(true));
// 添加目标元素
pipWindow.document.body.appendChild(figure);
});
}, false);
// 创建退出按钮
documentPictureInPicture.addEventListener("enter", (event) => {
const pipWindow = event.window;
const pipExitButton = pipWindow.document.createElement("button");
pipExitButton.className = "ui-button";
pipExitButton.type = "primary";
pipExitButton.textContent = "退出画中画";
pipExitButton.addEventListener("click", () => {
window.documentPictureInPicture.window.close();
});
pipWindow.document.body.append(pipExitButton);
});
这段代码处理示意了画中画模式的进入退出,还演示了如何在画中画窗口创建额外的元素以及构建样式。
需要注意的是,我原本打算插入<link>元素,直接链接一个CSS文件,结果发现无效,于是改为fetch CSS数据,使用<style>元素内联实现的。
以及,实际开发的时候,可以使用 async/await 语法减少Promise的嵌套使用。
画中画的实现其实就那几行代码,然后,画中画小窗口的关闭按钮这些都是可以自定义的,如何自定义使用,到时候让AI模型帮你处理就好了,我就不告诉大家是disallowReturnToOpener这个可选参数了。
接下来,看下兼容性:

Safari浏览器拖了后腿,不过桌面端本身就是Chrome的天下,因此,此特性还是可以大胆使用的,毕竟,就算浏览器不支持,也支持保持现状而已。
好了,就说这么多,博闻强识。
对了,今年开春到现在,都是博客和B站的碎片化输出,打算国庆后到春节前,弄一个前端在线学习的系列课,当然,要与时俱进,不然狗都不看。
拭目以待吧~

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