用Css3 实现React 动画的三种方法

html-css0205

用Css3 实现React 动画的三种方法,第1张

中级:

Css3中的帧动画

这次用react-transition-group做一个togglebutton控制div显示和隐藏的例子,首先我们需要安装react-transition-group,输入

组件中引入CSSTransition模块:

一旦动画入场,插件将会自动的在包裹住的标签上添加很多css样式,默认class名是boss-text,所以我们需要给CSSTransition标签加上classNames='boss-text',然后去css文件进行配置:

如果页面上一组dom都需要添加动画效果时我们需要在最外面再加一个TransitionGroup

我们只会用到一点点儿CSS布局的属性,虽然少,但既然要用到,就得把它们总结在这里,搞明白。

在了解CSS布局的相关属性之前,我们先了解一下 盒子模型——CSS会把每个组件都看作是一个盒子,每个盒子从内往外有三个部分:

组件的宽度和高度决定了它在屏幕上显示的尺寸,一个组件的宽度和高度就是它 外边框所包围矩形 的宽度和高度,iOS里也是这样的。

我们可以通过 width 和 height 属性给一个组件设置固定的宽度,不过需要注意 这两个属性只能接收数值,不能带单位,这是因为RN要自己去适配安卓和iOS的单位。

在RN里,我们想让一个组件自适应宽高,要分两步。 首先我们要让该组件撑满它的父视图,即如果我们想自适应宽度,就得先让子视图的宽度撑满它父视图的宽度,如果我们想自适应高度,就得先让子视图撑满它父视图的高度,如果我们宽度和高度都想自适应,就得先让子视图把它父视图的宽度和高度都撑满。然后才能像我们iOS里使用Masonry那样,通过给组件添加上边距、左边距、下边距、右边距等约束来实现宽度和高度的自适应。

这里再对第一步做个解释, item的 flex: 1 属性可以让item在主轴上撑满它的容器,容器的 alignItems: 'stretch' 属性 + item在侧轴上不设置空间可以让item在侧轴上撑满它的容器。 好像有点不明白是吧,我们再通过一个例子来说得明了点,RN里不是默认主轴为竖向、侧轴为横向嘛,我们就采取这个默认状态来举例子。 在RN里,默认主轴为竖向、侧轴为横向的状态下,如果我们想让某个组件自适应高度,就得首先在它自身上设置 flex: 1 属性,来让它的高度撑满它的父视图;如果我们想让某个组件自适应宽度,就得首先在它父视图身上设置 alignItems: 'stretch' 属性(当然默认就是这个值),这还不够,你还得确保不给这个组件设置 width 属性或者 width 属性的值设为 auto ;如果我们想让一个组件自适应宽高,则两者都得做。 当然,如果我们设置了主轴为横向、侧轴为竖向,其实也是一样的道理,自己捋一下就知道怎么做了。如果还是看不懂的话,没有关系,下面我们会详细谈到这几个属性,等你学了这几个属性,反过头来看这里就明白了。

Flex是Flexible Box的缩写,可译为灵活的、有弹性的盒子。那Flex布局就是Flexible Box布局的缩写,就译为弹性盒子布局,用来对一个盒子进行灵活的布局。

在了解Flex布局之前,我们得先了解一下Flex布局里的两对儿 重要的 概念。

采用Flex布局的组件,被称为Flex容器(flex container),简称容器(container)。

这个组件上的子组件,被称为Flex项目(flex item),简称item(项目)。

我们可以把容器和item看做是父视图和子视图的关系, 因此下文中的容器、父组件、父视图将是一样的概念,item、组件、子视图将是一样的概念,组件和视图将是一样的概念。

每个容器都有两根轴,一个是 主轴, 一个是 侧轴

容器里众多的 item自动沿主轴进行排列。

一个item在主轴方向上所占据的空间称为 main size ,一个item在侧轴方向上所占据的空间称为 cross size 。

RN里主轴的默认方向为竖向,侧轴为横向。 浏览器里主轴的默认方向为横向,侧轴为竖向,下面我们讲Flex布局的属性时会采用浏览器的状态,学会后对应到RN里,换个主轴方向就可以了。

用在容器上的属性,最常用的有 5个 而且这些属性还都不是从来设置容器自己的布局,而是用来设置容器里item的布局。

flex-direction 属性用来设置主轴的方向,即容器里item自动排列的方向。 在浏览器里,主轴的方向默认为横向,所以 flex-direction 属性的默认值为 row 。

它有4种取值。

justify-content 属性用来设置item在主轴上的对齐方式。

它有5种取值。

align-items 属性用来设置item在侧轴上的对齐方式。

它有5种取值。

默认情况下,一个容器只有一条主轴,所有的item都会沿着这条主轴自动排列下去,但是如果item太多了,一个屏幕长度的主轴根本串儿不下这么多item,那后面的item就会溢出屏幕,而界面还不是 ScrollView ,不能滚动,怎么办呢?

flex-wrap 属性用来设置换行,即当容器主轴上显示不下那么多的item时,我们可以让item换行来显示,并且一旦设置了换行,那么这个容器就不再是一条主轴了,而是会有多条平行的主轴。

它有3种取值。

align-content 属性用来设置多条主轴的对齐方式, 如果项目只有一根轴线,该属性不起作用, 即 align-content 属性要基于 flex-wrap 属性设置了换行才有使用的意义。

它有6种取值。

用在item上的属性,最常用的有 2个 这些属性都是用来设置item自己的布局。

在第1小节容器的属性,我们已经通过 align-items 属性统一设置了容器里item在侧轴上的对齐方式,但如果其中某个item不想遵循这个对齐方式怎么办呢?我们会常见到这种场景,10个里有8个item是一样的对齐方式,就有1、2个item特立独行,那我们就得用item自己的 align-self 属性来单独给它们设置对齐方式了。

align-self 属性用来设置单个item在侧轴上的对齐方式, 注意是给特立独行的单个item设置哦,而且是侧轴,它会覆盖它容器的 align-items 属性,当然如果不设置这个属性,默认值就为 auto ,即item会默认使用容器的 align-items 属性给它设置的对齐方式。

它有6种取值,除了 auto ,其他都与 align-items 属性完全一致。怎么说呢,item的 align-self 属性似乎是用来造反的,反抗它容器的 align-items 属性。

flex 属性接收一个整数值, flex 属性描述起来太麻烦了,可以自行去搜索加深理解,我这里只列举出它的用途及注意事项。

(1) flex 属性有三个用途

(2)使用 flex 属性需注意

iOS里怎么使用Masonry布局的,RN里就怎么使用 Flex布局+CSS布局 来做布局就行了,它们布局的思路是一样的, 一个组件也同样至少需要四个约束。

当然,它们之间有一个极其细小的差别,那就是:上面提到的“RN里组件自适应宽高分两步”,而iOS里Masonry才不管你什么撑满不撑满呢。

当然,它们之间有一个极其巨大的差别,那就是:iOS里是没有主轴和侧轴之分的,你可以在一个父视图上横向、竖向同时放控件,并同时完成横向和竖向的布局。但RN里有主轴和侧轴之分,我们添加的组件只会沿着主轴排列,不可能同时出现横向和竖向排列,并且布局的时候也只能是沿着主轴布局,不可能同时完成两个方向的布局。

下面我们就通过几个小练习,来对比一下Masonry布局和Flex布局+CSS布局的思路,加深对RN里怎么布局组件的理解。

Masonry:

Flex布局+CSS布局:

Masonry:

Flex布局+CSS布局:

Masonry:

Flex布局+CSS布局:

Masonry:

Flex布局+CSS布局:

参考博客:

阮一峰的《Flex布局:语法篇》

阮一峰的《Flex布局:实例篇》

CSS Modules 不是官方规范或浏览器中的实现,而是构建步骤中的一个过程(在 Webpack 或 Browserify 的帮助下),它改变了类名和选择器的作用域(即有点像命名空间)。

目的: 解决 CSS 中全局作用域的问题

在 React 中默认开启了 CSS Module,样式表文件需要以 xxx.module.sass/less/css 命名

我们也可以通过配置 webpack 来开启 CSS Module

webpack.config.js

localIdentName 可以定义生产的哈希类名,默认是 [hash:base64]

详细配置见: css-loader

localIdentName 选项的占位符有:

默认 CSS 的规则是全局生效的,任何一个组件下的 CSS 样式,都会影响其他组件中使用相同类名的地方。

style.css

App.js

Header.css

Header.js

index.js

此时我们的页面上展示的就是绿色的 Header 组件 和 Hello World

因为定义了两个相同的 title class,虽然是在不同的组件中导入,但是他们的类名是一样的,最终都会在全局作用域下生效,因为这两个组件都渲染在了页面上。

至于为什么会显示成绿色,因为 Header 组件是后导入的,所以 Header 的 title 样式就覆盖了 App 的 title 样式,这就是 CSS 层叠样式的概念了,此处不再赘述。(如果导入顺序换一下,那么就是红色了)

答案: 产生局部作用域的唯一方法,就是使用一个独一无二的 class 的名字,不会与其他选择器重名。这就是 CSS Modules 的做法。

这里就拿 React 项目来进行解释

在 React 中,默认是开启 CSS Module 的。但是对于样式表文件的命名一个约束。需要以 .module.less/css/sass 结尾

随意我们就可以这样改造一下 Header 组件,来使用 CSS Module 的功能。

效果: Header 组件 展示为绿色; Hello World 展示为红色。可以看到 Header 中相同类名的样式并没有影响到我们的 App 组件

此时在控制台中查看 HTML,发现我们 Header 组件中的 h2 标签上的 class 类名变成了 <h2class="_src_Header_module__title">Header 组件</h2>

同理在样式表文件中也变成了

App 组件

随机的 className 是可以配置的:通过 webpack.config.js 中配置 css-loader 的 localIdentName 选项来定义生成的随机类名

通常在我们的日常开发中有这种场景:

我们有一个自己的组件,但是这个组件使用了一些第三方的组件库,对于我们使用的第三方组件我们又想修改一下它的样式。

如果此时,我们直接在当前组件的样式文件中,通过定义一个和第三方组件相同类名的类(比如说 antd button 的 antdr-btn 类),然后写自己的样式:

Button.module.css

然后我在组件中导入

Button.js

此时我们会发现我们的修改并没有生效,为什么呢?原因就是最后我们导入的类名会被 css-loader 处理成一个随机的值,所以导致不再是 antdr-btn 。

那么我们如何实现在自定义组件中修改第三方组件的样式呢?

需要不对第三方组件的类名进行哈希,保留原始类名,才能起到样式覆盖的作用 :global

:global(.className)那么此时这个 className 即使是在组件的样式表中定义的也不会被添加 hash 值,所以就可以影响全局所有类名为 className 的样式

注意:

此时组件中对该类的样式修改会影响全局所有使用该类名的地方,所以为了将样式修改限制到本组件,一般推荐将:global 使用在组件自定义类名范围下,然后添加这个自定义类名到组件中

CSS Modules 还提供一种显式的局部作用域语法:local(.className),等同于.className(直接在样式文件中写.className)该类名在编译后会被添加 hash 值

在 CSS Modules 中,一个选择器可以继承另一个选择器的规则,这称为"组合"。

在 Header.module.css 中,让.title 继承.back 。

Header.module.css

Header.js

编译后

CSS

HTML

选择器也可以继承其他 CSS 文件里面的规则。

other.module.css

Header.module.css

注意:

导入的类名需要和被导入文件中的类名相同

编译之后的效果和 composes 同一个文件中的 class 效果相同