MoreRSS

site iconScarSu | 酥鱼修改

96女性程序员,18年至今全职从事前端开发工作,开发了一个浏览器插件NotionX
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

ScarSu | 酥鱼的 RSS 预览

View Transition API - 实现HTML视图的过渡动画

2024-09-19 08:00:00

触发视图动效的方法&时机:

  • 对于SPA,将更新DOM的方法updateDomTrigger作为参数传递给document.startViewTransition(updateDomTrigger)方法,浏览器会<u>先截取</u>当前页面DOM元素的快照(声明了 view-transition-name CSS属性的DOM元素,默认是:root),<u>再执行</u>updateDomTrigger方法,然后<u>再执行过渡动效</u>。

  • 对于MPA,添加以下CSS规则,过渡效果会在导航到下一个同源页面的时候自动触发。

    @view-transition {
      navigaion: auto;
    }
    

默认的过渡效果是<u>交叉淡化</u>:

旧视图快照opacity从1 到 0,新视图快照opacity从0 到 1。

如何自定义动效:

HTML会针对视图动效生成以下伪元素树:

::view-transition
└─ ::view-transition-group(root)
  └─ ::view-transition-image-pair(root)
      ├─ ::view-transition-old(root)
      └─ ::view-transition-new(root)
  • 可以通过在多个 DOM 元素上设置不同的 view-transition-name CSS属性,对不同的元素使用不同的自定义动效。因此,每一个 view-transition-name都对应一个view-transition-group(默认是root
  • view-transition-old指向动效前元素的静态快照
  • view-transition-new指向动效后元素的实时快照

具体的动画效果由CSS animation设置:


/* 只需要添加以下@view-transition规则,就会在切换页面时,触发默认的“淡化”动效 */
@view-transition {
    navigation: auto;
}

/* 自定义默认的动画行为 */
::view-transition-group(root) {
    animation-duration: 0.5s;
}

/* 创建自定义动画 */
@keyframes move-out {
    from {
        transform: translateY(0%);
    }
    to {
        transform: translateY(-100%);
    }
}
@keyframes move-in {
    from {
        transform: translateY(100%);
    }
    to {
        transform: translateY(0%);
    }
}

/* 将自定义动画应用到新旧元素 */
::view-transition-old(root) {
    animation: 0.4s ease-in both move-out;

}
::view-transition-new(root) {
    animation: 0.4s ease-in both move-in;
}

用JavaScript控制动效:

对于SPA,document.startViewTransition() 方法会返回一个 ViewTransition 对象实例,这个实例包含多个 promise:

对于MPA:

  • 旧页面在即将unload之前,会触发pageswap 事件,事件的event对象上的PageSwapEvent.viewTransition 属性包含了 ViewTransition 实例, PageSwapEvent.activation 包含当前切换页面的导航类型、当前文档和目标文档历史记录。
  • 新页面初次渲染时, 会触发pagereveal 事件,事件的event对象上的PageSwapEvent.viewTransition 属性包含了 ViewTransition 实例。
  • 要注意的是,为了确保处理pagereveal 事件的script脚本在渲染动效之前执行,需要给脚本添加 blocking=“render” 属性;
  • 另外,为了确保动效执行之前,元素是可视的,需要添加标签<link rel="expect" href="#lead-content" blocking="render" />,其中#lead-content指向对应元素。

浏览器兼容性

image.png

参考


本篇文章使用Notion创作,由我的自动化工具从Notion同步。

Stacking Context - DOM元素的层级关系

2024-09-04 08:00:00

不是z-index越大的元素越在上层

通过z-index来控制层级是很常见的前端需求,

但是你是否遇到过,无论z-index设置多大,仍然无法将一个元素移到最上层的情况?

这是因为z-index依赖于一个抽象的概念:Stacking Context

Stacking Context是什么?

stacking context是层叠上下文,决定了DOM元素上下层级关系

Stacking Context的根元素

每个层叠上下文都由一个元素创建,这个元素被称为stacking context的根元素

什么情况会创建Stacking Context?

  • html根元素
  • fixed定位的元素
  • 其他任何z-index不是默认值auto的元素(z-index=0也会创建层叠上下文)
  • CSS
    • 有透明度的元素:opacity<1
    • 有二维/三维变幻的元素:transform不为none,或者will-change
    • 有滤镜的元素:filter不为none
    • isolation: isolate;:创建一个新的堆叠上下文,隔离混合效果

Stacking Context之间的层级顺序

  • 对于同级(根元素是兄弟元素)的stacking context,层级由z-index值决定,z-index大的在上层;
  • 同级stacking context如果z-index相同,由DOM中出现的顺序决定,出现晚的在上层;
  • 对于嵌套的stacking context,子stacking context在上层;

Stacking Context内部层级顺序

  • 最底层:元素的背景色、背景图、边框
  • 子元素由底层到顶层的顺序:
    • z-index<0的元素
    • 块级盒子
    • 浮动盒子
    • 内联盒子
    • z-index:0或auto的定位盒子
    • z-index>0的元素

例子🌰

DOM结构:

div1
div2
div3
	-> div4
	-> div5
	-> div6

最终的层级顺序:

image.png

一个有用的浏览器扩展:CSS Stacking Context inspector

image.png

这个扩展在浏览器devtools中列出了网页上的stacking context。在遇到z-index和层级问题时,可以让我们更快的定位问题。


本篇文章使用Notion创作,由我的自动化工具从Notion同步。

2023热门JS语法新特性

2024-09-02 08:00:00

Nullish Coalescing

空值合并运算符(??),识别null和undefined值;MDN

const foo = null ?? 'default string';
console.log(foo);
// Expected output: "default string"

const baz = 0 ?? 42;
console.log(baz);
// Expected output: 0

Dynamic Import

动态引入,import函数支持异步引入js模块;MDN

import(moduleName)
import(moduleName, options)
await import('/modules/my-module.js')

Private Field

类私有属性,以#为前缀,无法在类的外部引用;MDN

class ClassWithPrivateField {
  #privateField
}

Logical Assignment

逻辑与赋值&&=,逻辑或赋值||=MDN

const a = { duration: 50, title: '' };

a.duration ||= 10;
console.log(a.duration);
// expected output: 50

a.title ||= 'title is empty.';
console.log(a.title);
// expected output: "title is empty"

let a = 1;
let b = 0;

a &&= 2;
console.log(a);
// Expected output: 2

b &&= 2;
console.log(b);
// Expected output: 0

Hashbang Comments

#! HashBang注释,用于在shell中执行js时,指定js解释器

是单行注释,只能写在js代码的第一行;MDN

#!/usr/bin/env node

console.log("Hello world");

error.cause

error实例中cause属性,指示了引起错误的具体原因;MDN

try {
  connectToDatabase();
} catch (err) {
  throw new Error('Connecting to database failed.', { cause: err });
}

array.toSpliced(),toSorted(),toReversed()

不改变数组原始值,创建新数据作为返回值

array.with()

相对于直接用[索引]更新数组,with方法不改变原始值、返回新数组

const arr = [1, 2, 3, 4, 5];
console.log(arr.with(2, 6)); // [1, 2, 6, 4, 5]
console.log(arr); // [1, 2, 3, 4, 5]

本篇文章使用Notion创作,由我的自动化工具从Notion同步。

Notion到博客的自动化更新

2023-07-29 08:00:00

我在上周的文章程序员一定要有自己的博客中写了我博客工具链的5步:

  1. 在趁手的工具写笔记(Notion PC端、移动端)
  2. 工具将笔记转化为md文档
  3. 自动推送md文档到我的博客git仓库
  4. git仓库更新,触发部署工具(netlify、vercel、jenkins)自动部署
  5. 更新博客

当时只实现了其中1245步,经过两天的探索我完成了第3步,让这个工具链实现了完整的自动化,这篇文章大致介绍一下。

从Notion到Git仓库

我的博客代码仓库使用Github托管,Github提供了一个十分有用的自动化工作流的工具:Github Actions

Github Actions 可以定时执行我提供的脚本,通过脚本,实现了从Notion到代码仓库的更新。大致逻辑是这样的:

  1. Github Actions 负责每天凌晨00:00执行脚本
  2. 在脚本中,使用Notion API接口获取Notion特定数据库的数据,筛选出前一天更新的文章
  3. 为每一篇文章,在代码仓库创建一个md文件
  4. 将文章的attributes,转化为博客md文件的frontmatter
  5. 将文章正文的md字符串,转化为md文件的正文内容
  6. 将所有md文件通过git命令提交到github仓库中
  7. 如果脚本执行失败,github会发邮件通知我

通过以上脚本,实现了 notion文章数据库到git仓库的更新。

脚本执行的频率是可以自由更改的,我偏向于损失一些时效性,保证环境稳定性,所以选择了一天一更新。

以上脚本可以在我的博客开源仓库中看到,链接在这里这里

由于我们的notion数据库字段、md文件的frontmatter配置很可能不同,我的脚本无法直接被你复用,有几个需要考虑变更的点:

  • 在你的git仓库中,需要配置几个脚本中需要使用的secrets。配置的地方在你的github仓库首页 - settings - secrests and variables - actions,参数名是NOTION_DATABASE_IDNOTION_KEY,前者是你在Notion中文章所在数据库的id(可以通过copy link获得),后者是你开通的notion api的key(notion api的开通详见这个文档)。
  • 从notion page attributes到md frontmatter的映射,可以在这个脚本postToMDFile中找到,逻辑比较简单,按照你的需求修改即可。
  • 脚本中的git信息,例如name、email、branch 可以按照你的需求修改。
  • 按照你的需求筛选notion数据,filter写在这个脚本getPosts中。
  • 有一个可能踩坑的点在于,必须要将workflow读写仓库的权限开启,否则脚本会因为缺少git push权限而报错,开启权限的地方在:github仓库首页-settings-actions-general-workflow permission - 开启read and write permissions
  • 脚本的执行周期,可以在上述yml脚本的cron字段中配置,注意脚本中的时间使用的是UTC时间,要考虑当地时区的时间差,例如UTC=北京时间-8,我希望在凌晨0点执行,所以写的小时是16。

从Git仓库到博客网站

这一步我使用的是Vercel工具,监听Git仓库的push事件,自动拉取代码更新,打包部署到网站。

这一步Vercel的配置很简单,推荐查看官方文档,不做赘述。


本篇文章使用Notion创作,由我的自动化工具从Notion同步。

时钟准确的定时轮询函数

2023-07-28 08:00:00

浏览器原生的setTimeout(fn,timeout)函数的原理,是保证timeout时间后,将回调函数fn加入event loop的消息队列,不是保证timeout时间后一定执行。

回调函数加入的消息队列后,实际的执行时间要等到 调用栈中已有函数 + 消息队列已有消息执行完毕。

所以回调函数的实际执行时间与设定的timeout有偏差。

setInterval函数也同样有偏差。

以下实现了一个时间准确的定时轮询函数。

setClockInterval 函数

/**
 * setClockInterval是时钟准确的定时轮询函数(原生setInterval时钟不准确)
 * @param func 与原生setInterval参数一致
 * @param interval 与原生setInterval参数一致
 * @return timer 类型为number
 */
const timerMap = new Map()
export function setClockInterval(func: Function, interval: number) {
  let start: number
  let tick: number
  let clockTimer: number | null
  const timerId = Math.floor(Math.random() * 1e10)
  const recurFunc = () => {
    func()
    const realExcuTime = new Date().getTime()
    if (!start) {
      start = realExcuTime
    }
    tick = tick || start

    // bias是本次实际执行时间戳 与 理论执行时间戳tick之间的偏差
    // bias一定>=0,因为浏览器的setTimeout setInterval只会准时或者延迟执行,不会提前执行
    // 经过本地浏览器统计 bias平均值在10ms以内,最大值在20ms左右
    const bias = realExcuTime - tick
    console.log(`实际执行时间戳${realExcuTime},理论计划执行时间戳${tick},本次偏差${bias}ms`);

    // 下次tick时间戳
    tick += interval

    // 因为本次实际执行时间有bias的延迟,所以下次执行提前
    clockTimer = window.setTimeout(recurFunc, interval - bias)
    timerMap.set(timerId, clockTimer)
  }
  recurFunc()
  return timerId
}

clearClockInterval 函数

/**
 * clearClockInterval 用于终止 setClockInterval 的定时轮询
 * @param timer 是 setClockInterval 的返回值
 */
export function clearClockInterval(timerId: number) {
  if (timerMap.has(timerId)) {
    window.clearTimeout(timerMap.get(timerId))
  } else {
    console.warn(`timerId${timerId}not found`);
  }
}

demo执行效果

bias平均值在10ms以内,最大值在20ms左右

09:45:39.557 util.ts:23 实际执行时间戳1690508739557,理论计划执行时间戳1690508739550,本次偏差7ms
09:45:40.555 util.ts:23 实际执行时间戳1690508740555,理论计划执行时间戳1690508740550,本次偏差5ms
09:45:41.561 util.ts:23 实际执行时间戳1690508741561,理论计划执行时间戳1690508741550,本次偏差11ms
09:45:42.559 util.ts:23 实际执行时间戳1690508742559,理论计划执行时间戳1690508742550,本次偏差9ms
09:45:43.559 util.ts:23 实际执行时间戳1690508743559,理论计划执行时间戳1690508743550,本次偏差9ms
09:45:44.560 util.ts:23 实际执行时间戳1690508744560,理论计划执行时间戳1690508744550,本次偏差10ms
09:45:45.556 util.ts:23 实际执行时间戳1690508745555,理论计划执行时间戳1690508745550,本次偏差5ms
09:45:46.552 util.ts:23 实际执行时间戳1690508746552,理论计划执行时间戳1690508746550,本次偏差2ms
09:45:47.562 util.ts:23 实际执行时间戳1690508747562,理论计划执行时间戳1690508747550,本次偏差12ms
09:45:48.561 util.ts:23 实际执行时间戳1690508748561,理论计划执行时间戳1690508748550,本次偏差11ms

程序员一定要有自己的博客

2023-07-19 08:00:00

最近用astro对自己的博客进行了重构,投入了很大的热情和很多的时间,导致耽误了这个月的读书任务,于是反思了一下是否值得这么做。

为什么要有博客

博客之于程序员意义重大,因为,输出是一个程序员的基本素养和技能,开源分享是科学技术发展和人类社会进步的重要美德和推动力,而博客正是输出分享的重要途径之一。

为什么输出分享对程序员来说十分重要,我有以下几点看法。

  • 从技术角度考虑,技术的提升依赖于专业知识的学习以及实际经验的积累,而人的大脑需要不断地进行重复记忆,才能将这些知识经验留在自己的知识库里。因此,无论是学习新知识还是通过实践得到的经验,有了即时的输出记录,才便于复盘与巩固。

  • 在实际工作中,当我们面临复杂大型的项目,和其中大量的代码时,如果只根据个人喜好而不根据代码规范去开发,写完后不留存开发文档以及接口文档,必然给项目的维护带来更高的代价,给团队中的其他开发者带来困扰。因此,代码规范和开发文档的输出也相当必要。

如果你还不相信,坚持输出给程序员带来好处的例子很多,我随意举几个。

  • 例如 IT 圈出名的阮一峰张鑫旭,stormzhang(这位已经不再分享技术了)等等大佬,他们有今天这种影响力的一个重要因素就是输出分享;

  • 我也曾不止一次收到前辈的建议,前端圈的小爝大佬在知乎的某个回答当中提到过“长期坚持技术输出和总结分享”在找工作面试中是一个亮点和加分项;

  • “前端桃园”公号的运营者桃翁也十分提倡坚持输出,他在他星球小圈子中给我们分享过他自己 因为坚持输出提升了影响力 而多次收到阿里面试邀请的经历。

还有很多例子,不一一例举,分享本文的原因也就写到这,不再赘述。

(当然输出分享的途径很多,本文以介绍博客为目的,如果选择其他途径可以忽略下文)

我选择博客工具链的目标

现在网络上有各式各样的博客,有基于第三方的平台(如博客园、csdn 等),自带用户和流量,但是要受平台约束和限制;

也有可供个人搭建的工具(如 Hexo、Ghost、wordpress、jekyll 等),自由度高,但是需要从0开始搭建和积累流量;

选择一个最适合自己的最重要。

作为一个博客,我最重视的有两个方面:

  1. 自由,可自定义,可个性化。

因为每个人追求的博客风格不同,想要展示的内容和格式也有所不同。

我希望能自由地表达自己想说的话。

  1. 高效转化。

我指的转化,是你的输出原文档 到 博客文本的转化,这一转化的过程对于想要存档原文,或不习惯于博客编辑器,不习惯于博客网页格式的人之分重要。

我推崇用 markdown 来写笔记,因为 markdown 足够简约优雅,兼容性也十分强,

Markdown 是一种轻量级的「标记语言」,通常为程序员群体所用,目前它已是全球最大的技术分享网站 GitHub 和技术问答网站 StackOverFlow 的御用书写格式。

非技术类笔记用户,千万不要被「标记」、「语言」吓到,Markdown 的语法十分简单,常用的标记符号不超过十个,用于日常写作记录绰绰有余,不到半小时就能完全掌握。

就是这十个不到的标记符号,却能让人优雅地沉浸式记录,专注内容而不是纠结排版,达到「心中无尘,码字入神」的境界。

总结来说,我目标的工具链是这样的:

  1. 在趁手的工具写笔记(Notion PC端、移动端)
  2. 工具将笔记转化为md文档
  3. 自动推送md文档到我的博客git仓库
  4. git仓库更新,触发部署工具(netlify、vercel、jenkins)自动部署
  5. 更新博客

目前已经实现了1,2,4,5,目前正在研究第3步,成功后我就不会再对工具链投入时间,而是把注意力放到内容上。

写在最后

以技术为目标的人,切忌浮躁。

莫逞他人嘴上快,莫争浮世虚功名,心无旁骛,沉下心来钻研技术就好。