Monday, April 18, 2022

SegmentFault 最新的文章

SegmentFault 最新的文章


“制裁”蔓延:Github 或封禁 41 万俄罗斯开发者账号!

Posted: 17 Apr 2022 11:17 PM PDT

据俄罗斯媒体 Habr 报道称,近段时间至少有几十个 GitHub 账户在当地被屏蔽、禁用、清空内容等限制。这其中,包括 Sberbank、 Alfa-Bank 银行、其他公司以及个人开发商的账户。

据悉,早在今年 2 月 28 日,俄罗斯《生意人报》就曾报道称,收到了"GitHub 正在考虑限制俄罗斯软件开发者访问开源软件源代码储存库"的消息。

紧接着 4 月初,就有媒体报道称,美国财政部已对俄罗斯联邦储蓄银行和俄罗斯最大的私人银行 Alfa-Bank 实施了全面封锁制裁。制裁措施包括冻结银行资产,禁止美国公民和公司与该银行做生意等。

对此事件,研究员 Sergey Bobrov 表示,GitHub 也屏蔽了他的个人 GitHub 账户。关键是,他和以上任何公司都没啥关系。

据透露,当 GitHub 阻止个人帐户时,它会得到一个暂停的标记。然而,当它阻止一个公司账户时,它的状态为"已标记"。此外,当阻止公司帐户时,对存储库数据的访问可能会保留几个小时;而当 GitHub 阻止个人帐户时,用户会立即失去访问权限。阻止时,问题和对其他存储库的请求将消失。而一些被屏蔽的账户不包含私人存储库,只包含公共存储库。

一些用户收到了相关电子邮件,显示他们可以从受制裁的地区管理自己的帐户:当用户浏览电子邮件中的链接时,必须填写一份申诉书。它们还应表明该公司或用户与North Korea、 Syria、 Crimea、the LPR 以及 the DPR 的关系。

"这并不奇怪。我们的意思是,在 2022 年 3 月 2 日,GitHub web 服务托管 IT 项目的代表结束了关于是否应该阻止俄罗斯开发人员的争议。该平台表示将监控情况,不会封锁所有人。在此之前,来自世界各地的开发者讨论了在俄罗斯联邦禁用 GitHub 的必要性。"

对此封禁事件,GitHub 方面公开表态称:"GitHub 的愿景是成为所有开发商的家园,无论他们住在哪里。我们认真履行政府授权的义务,包括遵守严格的新出口管制,旨在严格限制俄罗斯获得维持其'侵略性'军事能力所需的技术和其他物品"。

GitHub 还表示,他们将认真履行职责,彻底审查政府授权,确保用户和客户不会受到授权范围以外的影响。这包括在 GitHub 相互关联的社区中保护开放协作和信息自由流动,以支持通信、人道主义工作和组织变革。"

据 DAXX 统计数据显示,截止 2021 年俄罗斯共有开发者 41.2 万。若按照欧美所言,俄罗斯"实行军民不分家举国体制",那么此次"GitHub 账号封禁"事件将影响全部开发者。

目前,网上关于该事件的讨论依旧火热,事件后续本站也将持续关注。对于此事件,您怎们看?欢迎在评论区交流互动。

Amazing!巧用 CSS 视差实现酷炫交互动效

Posted: 17 Apr 2022 07:35 PM PDT

本文将介绍利用 CSS 实现滚动视差效果的一个小技巧,并且,利用这个技巧来制作一些有意思的交互特效。

关于使用 CSS 实现滚动视差效果,在之前有一篇文章详细描述过具体方案 - CSS 实现视差效果,感兴趣的同学可以先看看这篇文章。

这里,会运用上这样一种纯 CSS 的视差技巧:

使用 transform: translate3d 实现滚动视差

这里利用的是 CSS 3D,实现滚动视差效果。

原理就是:

  1. 我们给容器设置上 transform-style: preserve-3dperspective: xpx,那么处于这个容器的子元素就将位于3D空间中,
  2. 再给子元素设置不同的 transform: translateZ(),这个时候,不同元素在 3D Z轴方向距离屏幕(我们的眼睛)的距离也就不一样
  3. 滚动滚动条,由于子元素设置了不同的 transform: translateZ(),那么他们滚动的上下距离 translateY 相对屏幕(我们的眼睛),也是不一样的,这就达到了滚动视差的效果。
关于 transform-style: preserve-3d 以及 perspective 本文不做过多篇幅展开,默认读者都有所了解,还不是特别清楚的,可以先了解下 CSS 3D。

核心代码表示就是:

<div class="g-container">     <div class="section-one">translateZ(-1)</div>     <div class="section-two">translateZ(-2)</div>     <div class="section-three">translateZ(-3)</div> </div>
html {     height: 100%;     overflow: hidden; }  body {     perspective: 1px;     transform-style: preserve-3d;     height: 100%;     overflow-y: scroll;     overflow-x: hidden; }  .g-container {     height: 150%;      .section-one {         transform: translateZ(-1px);     }     .section-two {         transform: translateZ(-2px);     }     .section-three {         transform: translateZ(-3px);     } } 

总结就是父元素设置 transform-style: preserve-3dperspective: 1px,子元素设置不同的 transform: translateZ,滚动滚动条,效果如下:

css3dparallax

CodePen Demo -- CSS 3D parallax

很明显,当滚动滚动条时,不同子元素的位移程度从视觉上看是不一样的,也就达到了所谓的滚动视差效果。

借助 CSS 视差实现酷炫交互动效

OK,有了上面的铺垫,我们来看看这样两个有趣的交互效果。由群里的日服第一切图仔 wheatup 友情提供。

先来看第一个效果:

效果是一种文本交替在不同高度的层展示,并且在滚动的过程中,会有明显的 3D 视差效果。

这个效果并不困难,核心就在于:

  1. 利用了 transform-style: preserve-3dperspective 构建不同的层次效果,制作视差效果
  2. 利用元素的 ::before::after 构建了 3D 的效果

我们看一个最小化 DEMO:

<div class="g-container">     <div class="g-box"></div>     <div class="g-box"></div>     <div class="g-box"></div> </div>
.g-container {     height: 150vh;     perspective: 600px; }  .g-box {     width: 200px;     height: 200px;     background: #999;     transform-style: preserve-3d;      &::before,     &::after {         content: "";         position: absolute;         right: 0;         left: 0;         transform-style: preserve-3d;         height: 200px;         background-color: #ccc;     }     &::before {         transform-origin: top center;         top: 0;         transform: rotateX(-90deg);     }     &::after {         transform-origin: bottom center;         bottom: 0;         transform: rotateX(90deg);     } }

滚动 g-container 容器,即可得到一种 3D 效果:

由于还需要视差效果,我们需要给不同的层赋予不同的 translateZ(),我们稍微改造下代码,给每个 g-box 中间,再加多一层正常的 div,再给每个 g-box 加上一个 translateZ()

<div class="g-container">     <div class="g-box"></div>     <div class="g-normal"></div>     <div class="g-box"></div>     <div class="g-normal"></div>     <div class="g-box"></div> </div>
.g-container {     width: 400px;     height: 150vh;     perspective: 800px; }  .g-normal {     width: 200px;     height: 200px;     background: #666;     transform-style: preserve-3d; }  .g-box {     width: 200px;     height: 200px;     background: #999;     transform-style: preserve-3d;     transform: translate3d(0, 0, 200px);      &::before,     &::after {         // ... 保持不变     } }
  1. 由于 g-boxg-normal 的 translateZ 值不同,所以滚动的过程中会出现视差效果
  2. 由于 g-boxtranslateZ 值为 translateZ(200px),两个伪元素的 rotateX 为正负 90deg,且高度为 200px,因此 g-boxg-normal 刚好可以通过 g-box 的两个伪元素衔接起来

最终,效果就是如上所示:

DEMO 的完整代码:CodePen Demo - 3D Parallax Scroll

CSS 滚动视差动画 2

OK,下面第二个滚动视差动画,也非常的有意思,想看看原版,也是来自于 wheatup 的 CodePen:

CodePen Demo -- 3D Chat Viewer

这里核心还是借助了 CSS 3D 的能力,但是由于使用的是滚动触发动画效果,并且有一定的从模糊到清晰的渐现效果,因此还是有一定的 JavaScript 代码。

感兴趣的可以看看上述的源码。

本文将尝试使用 CSS @Property 和 CSS 最新的特性 @scroll-timeline 还原该效果借助 JavaScript 实现的部分。

当然,首先不管是否需要借助 JavaScript,核心的 3D 部分使用的都是 CSS。

我们首先需要这样一个结构:

<div class="g-wrapper">     <div class="g-inner">         <div class="g-item"></div>         <div class="g-item"></div>         // ... 重复 N 个     </div> </div>
.g-wrapper {     width: 100vw;     height: 100vh; } .g-inner {     position: relative;     height: 100%;     display: flex;     flex-direction: column;     justify-content: center;     align-items: center;     gap: 10px; } .g-item {     width: 300px;     height: 100px;     background: #000; }

大概是这个效果:

然后,我们添加一些 CSS 3D 变换:

.g-wrapper {     // ... 与上述代码保持一致     perspective: 200px;     transform-style: preserve-3d; } .g-inner {     // ... 与上述代码保持一致     transform-style: preserve-3d;     transform: translateY(calc(-50% + 100px)) translateZ(0) rotateX(90deg);     transform-origin: bottom center; }

就能得到这样一种视角的效果:

由于容器 g-inner 进行了一个绕 X 轴的 90deg 翻转,也就是 rotateX(90deg),所以,我们再给 g-item 一个反向的旋转,把卡片翻转回来:

.g-wrapper {     // ... 与上述代码保持一致     perspective: 200px;     transform-style: preserve-3d; } .g-inner {     // ... 与上述代码保持一致     transform-style: preserve-3d;     transform: translateY(calc(-50% + 100px)) translateZ(0) rotateX(90deg);     transform-origin: bottom center; } .g-item {     // ... 与上述代码保持一致     transform: rotateX(-90deg); }

就能得到这样一种视角的效果:

此时,我们给容器一个赋予一个 translateZ 的动画:

.g-inner {     animation: move 10s infinite linear; } @keyframes move {     100% {         transform: translateY(calc(-50% + 100px)) translateZ(calc(100vh + 120px)) rotateX(90deg);     } }

这样,整个动画的雏形就完成了,通过控制父元素的 perspective 大小和容器的 translateZ,得到了一种不断向视角面前位移的动画效果:

CodePen Demo -- CSS 3D Effect Demo

结合 CSS @scroll-timeline,利用 CSS 控制滚动与动画

那怎么利用 CSS 再把这个动画和滚动操作结合起来呢?

在前不久,我介绍过 CSS 的一个重磅特性 来了来了,它终于来了,动画杀手锏 @scroll-timeline,利用它可以实现 CSS 动画与滚动操作的结合,我们利用它改造一下代码:

<div class="g-scroll" id="g-scroll"></div> <div class="g-wrapper">     <div class="g-inner">         <div class="g-item">Lorem ipsum dolor sit amet consectetur adipisicing elit.</div>         // ... 重复 N 个     </div> </div>
@property --phase {   syntax: '<length>';   inherits: false;   initial-value: 15px; } .g-scroll {     width: 100%;     height: 1000vh; } .g-wrapper {     position: fixed;     width: 100vw;     height: 100vh;     perspective: 200px;     transform-style: preserve-3d; } .g-inner {     position: relative;     height: 100%;     // 省略一些 flex 布局代码,与上文一致     transform-style: preserve-3d;     transform: translateY(calc(-50% + 100px)) translateZ(var(--phase)) rotateX(90deg);     transform-origin: bottom center;     animation-name: move;     animation-duration: 1s;     animation-timeline: box-move; } .g-item {     width: 300px;     height: 200px;     color: #fff;     background: #000;     transform: rotateX(-90deg); } @scroll-timeline box-move {     source: selector("#g-scroll");     orientation: "vertical"; } @keyframes move {     0% {         --phase: 0;     }     100% {         --phase: calc(100vh + 100px);     } }

这里相比上述的 DEMO,主要添加了 @scroll-timeline 的代码,我们增加了一个超长容器 .g-scroll,并且把它的滚动动作使用 @scroll-timeline box-move {} 规则和 animation-timeline: box-move 绑定了起来,这样,我们可以使用滚动去触发 @keyframes move {} CSS 动画。

效果如下:

在原效果中,还有一些使用 JavaScript 结合滚动距离控制的模糊的变化,这个,我们使用 backdrop-filter: blur() 也可以简单模拟。我们再简单添加一层 g-mask

<div class="g-scroll" id="g-scroll"></div> <div class="g-wrapper">     <div class="g-mask"></div>     <div class="g-inner">         <div class="g-item">Lorem ipsum dolor sit amet consectetur adipisicing elit.</div>         // ... 重复 N 个     </div> </div>
// 其他保持一致 .g-mask {     position: fixed;     width: 100vw;     height: 100vh;     backdrop-filter: blur(5px);     transform: translateZ(0); }

这样,基本就还原了原效果,并且,我们用且仅使用了 CSS:

CodePen Demo -- Pure CSS Scroll Animation 2(Chrome Only && Support ScrollTimeline)

总结一下

当然,本文后一个效果中,使用了非常多目前兼容性还非常差的 CSS 属性,尤其是 @scroll-timeline,仍然处于非常早期的阶段,兼容性一片红。但是不妨碍我们学习、感受 CSS 的美好。

要完全读懂本文,可能有一些前置知识需要了解,根据需要,你可以延伸阅读下:

好了,本文到此结束,希望对你有帮助 :)

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 😄

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

搞懂 TypeScript 中的映射类型(Mapped Types)

Posted: 16 Apr 2022 09:40 PM PDT

本文会和大家详细介绍 TypeScript 中的映射类型(Mapped Type),看完本文你将学到以下知识点:

  • 数学中的映射和 TS 中的映射类型的关系;
  • TS 中映射类型的应用;
  • TS 中映射类型修饰符的应用;

接下来会先从「数学中的映射」开始介绍。

本文使用到的 TypeScript 版本为 v4.6.2

如果你对 TypeScript 还不熟悉,可以看下面几篇资料:

  1. 一份不可多得的 TS 学习指南(1.8W字)
  2. 了不起的 TypeScript 入门教程

一、什么是映射?

在学习 TypeScript 类型系统时,尽量多和数学中的集合类比学习,比如 TypeScript 中的联合类型,类似数学中的并集等。

在数学中,映射是指两个元素的集合之间元素相互对应的关系,比如下图:

image.png
(来源:https://baike.baidu.com/item/%E6%98%A0%E5%B0%84/20402621

可以将映射理解为函数,如上图,当我们需要将集合 A 的元素转换为集合 B 的元素,可以通过 f函数做映射,比如将集合 A 的元素 1对应到集合 B 中的元素 2
这样就能很好的实现映射过程的复用

二、TypeScript 中的映射类型是什么?

1. 概念介绍

TypeScript 中的映射类型和数学中的映射类似,能够将一个集合的元素转换为新集合的元素,只是 TypeScript 映射类型是将一个类型映射成另一个类型

在我们实际开发中,经常会需要一个类型的所有属性转换为可选类型,这时候你可以直接使用 TypeScript 中的 Partial工具类型:

type User = {   name: string;   location: string;   age: number; }  type User2 = Partial<User>; /*   User2 的类型:      type User2 = {       name?: string | undefined;       location?: string | undefined;       age?: number | undefined;   } */

这样我们就实现了将 User类型映射成 User2类型,并且将 User类型中的所有属性转为可选类型。

image.png

2. 实现方法

TypeScript 映射类型的语法如下:

type TypeName<Type> = {   [Property in keyof Type]: boolean; };

我们既然可以通过 Partial工具类型非常简单的实现将指定类型的所有属性转换为可选类型,那其内容原理又是如何?

我们可以在编辑器中,将鼠标悬停在 Partial名称上面,可以看到编辑器提示如下:

image.png

拆解一下其中每个部分:

  • type Partial<T>:定义一个类型别名 Partial和泛型 T
  • keyof T:通过 keyof操作符获取泛型 T中所有 key,返回一个联合类型(如果不清楚什么是联合类型,可以理解为一个数组);
type User = {   name: string;   location: string;   age: number; }  type KeyOfUser = keyof User; // "name" | "location" | "age"
  • in:类似 JS 中 for...in中的 in,用来遍历目标类型的公开属性名;
  • T[P]:是个索引访问类型(也称查找类型),获取泛型 TP类型,类似 JS 中的访问对象的方式;
  • ?:将类型值设置为可选类型;
  • { [P in keyof T] ?: T[P] | undefined}:遍历 keyof T返回的联合类型,并定义用 P变量接收,其每次遍历返回的值为可选类型的 T[P]

这样就实现了 Partial工具类型,这种操作方法非常重要,是后面进行 TypeScript 类型体操的重要基础。

关于类型体操的练习,有兴趣可以看看这篇文章:
《这 30 道 TS 练习题,你能答对几道?》https://juejin.cn/post/7009046640308781063

三、映射类型的应用

TypeScript 映射类型经常用来复用一些对类型的操作过程,比如 TypeScript 目前支持的 21 种工具类型,将我们常用的一些类型操作定义成这些工具类型,方便开发者复用这些类型。

所有已支持的工具类型可以看下官方文档:
https://www.typescriptlang.org/docs/handbook/utility-types.html

下面我们挑几个常用的工具类型,看下其实现过程中是如何使用映射类型的。

在学习 TypeScript 过程中,推荐多在官方的 Playground 练习和学习:
https://www.typescriptlang.org/zh/play

1. Required 必选属性

用来将类型的所有属性设置为必选属性

实现如下:

type Required<T> = {     [P in keyof T]-?: T[P]; };

使用方式:

type User = {   name?: string;   location?: string;   age?: number; }  type User2 = Required<User>; /*   type User2 = {       name: string;       location: string;       age: number;   } */  const user: User2 = {   name: 'pingan8787',   age: 18 } /*   报错:   Property 'location' is missing in type '{ name: string; age: number; }'   but required in type 'Required<User>'. */

这边的 -?符号可以暂时理解为"将可选属性转换为必选属性",下一节会详细介绍这些符号。

2. Readonly 只读属性

用来将所有属性的类型设置为只读类型,即不能重新分配类型。

实现如下:

type Readonly<T> = {   readonly [P in keyof T]: T[P]; }

使用方式:

type User = {   name?: string;   location?: string;   age?: number; }  type User2 = Readonly<User>; /*   type User2 = {       readonly name?: string | undefined;       readonly location?: string | undefined;       readonly age?: number | undefined;   } */  const user: User2 = {   name: 'pingan8787',   age: 18 }  user.age = 20; /*   报错:   Cannot assign to 'age' because it is a read-only property. */

3. Pick 选择指定属性

用来从指定类型中选择指定属性并返回

实现如下:

type Pick<T, K extends keyof T> = {   [P in K]: T[P]; }

使用如下:

type User = {   name?: string;   location?: string;   age?: number; }  type User2 = Pick<User, 'name' | 'age'>; /*   type User2 = {       name?: string | undefined;       age?: number | undefined;   } */  const user1: User2 = {   name: 'pingan8787',   age: 18 }  const user2: User2 = {   name: 'pingan8787',   location: 'xiamen', // 报错   age: 18 } /*   报错   Type '{ name: string; location: string; age: number; }' is not assignable to type 'User2'.   Object literal may only specify known properties, and 'location' does not exist in type 'User2'. */

4. Omit 忽略指定属性

作用类似与 Pick工具类型相反,可以从指定类型中忽略指定的属性并返回。

实现如下:

type Omit<T, K extends string | number | symbol> = {   [P in Exclude<keyof T, K>]: T[P]; }

使用方式:

type User = {   name?: string;   location?: string;   age?: number; }  type User2 = Omit<User, 'name' | 'age'>; /*   type User2 = {       location?: string | undefined;   } */  const user1: User2 = {   location: 'xiamen', }  const user2: User2 = {   name: 'pingan8787', // 报错   location: 'xiamen' } /*   报错:   Type '{ name: string; location: string; }' is not assignable to type 'User2'.   Object literal may only specify known properties, and 'name' does not exist in type 'User2'. */

5. Exclude 从联合类型中排除指定类型

用来从指定的联合类型中排除指定类型

实现如下:

type Exclude<T, U> = T extends U ? never : T;

使用方式:

type User = {   name?: string;   location?: string;   age?: number; }  type User2 = Exclude<keyof User, 'name'>; /*   type User2 = "location" | "age" */  const user1: User2 = 'age'; const user2: User2 = 'location'; const user3: User2 = 'name';  // 报错 /*   报错:   Type '"name"' is not assignable to type 'User2'. */

四、映射修饰符的应用

在自定义映射类型的时候,我们可以使用两个映射类型的修饰符来实现我们的需求:

  • readonly修饰符:将指定属性设置为只读类型
  • ?修饰符:将指定属性设置为可选类型

前面介绍 ReadonlyPartial工具类型的时候已经使用到:

type Readonly<T> = {   readonly [P in keyof T]: T[P]; }  type Partial<T> = {   [P in keyof T]?: T[P] | undefined; }

当然,也可以对修饰符进行操作:

  • +添加修饰符(默认使用);
  • -删除修饰符;

比如:

type Required<T> = {     [P in keyof T]-?: T[P]; // 通过 - 删除 ? 修饰符 };

也可以放在前面使用:

type NoReadonly<T> = {   -readonly [P in keyof T]: T[P]; // 通过 - 删除 readonly 修饰符 }

五、总结

本文从数学中的映射作为切入点,详细介绍 TypeScript 映射类型(Mapped Type)并介绍映射类型的应用和修饰符的应用。

在学习 TypeScript 类型系统时,尽量多和数学中的集合类比学习,比如 TypeScript 中的联合类型,类似数学中的并集等。

学好映射类型,是接下来做类型体操中非常重要的基础~~

参考资料

  1. TypeScript 文档-映射类型:https://www.typescriptlang.org/docs/handbook/2/mapped-types.html
  2. TypeScript 工具类型: https://www.typescriptlang.org/docs/handbook/utility-types.html

No comments:

Post a Comment