首页 / 体育 / 正文

header card(移动端重构实战系列:0-4 章)

放大字体  缩小字体 来源:大尺寸触摸屏 2026-04-15 13:33  浏览次数:6

”本系列教程为实战教程,是自己移动端重构经验及思想的一次总结,也是对sheral UI的一次全方位剖析,首发在imweb和w3cplus两大站点及“前端Talk”微信公众号,其余所有标注或没有标注来源的均为转载。“

0.sandal & sheral

简单来说,sandal是基于sass的一个移动端css的基础库,提供了一些基础的重置,常用的mixin,如flex布局,等分,水平垂直居中,常用图标等,基于它你可以非常方便快速地扩展出你需要的UI组件,其整体结构设计如下图:

_function.scss集成了所有的基础功能,并且不带任何样式,而_core.scss则在function的基础上加入了重置样式,ext文件夹则是三个扩展文件,可根据个人需要自由导入,具体介绍及使用请参考sandal 文档

sheral是基于sandal扩展的UI组件库,目前包括了btn,dialog,header,card,form,toast,line,media,progress等常用的25+组件。你可以直接调用,也可以根据自己的需求定制你的组件。

sandal与sheral的关系,就如jquery与其插件的关系。所以退一万步说如果sheral的UI真的不合你意,你也可以基于sandal提供的基础功能,快速构建一套你自己的UI库。这也是我把这两个分开开发的原因。

放肆还是克制

如果要适应各种场景,就必然会增加代码量,而各种情况又不一定能全部用上,那冗余的代码必然是个累赘,要是换个人开发那更是不敢动了;而如果太简单,必然又无法发挥一个UI库的作用,所以这必然是一个纠结的问题。

为了遵循克制这原则,在组件的头部,我们经常会看见一些带有switch标识的开关组件,有默认会true的,也有为false的,你可以根据你的需要选择开或者关来决定是否生成该样式。

其他说明

  1. 如无特别申明,所有的@mixin均定义在sandal的_mixin.scss中。

  2. 1.基础知识

    既然是新的开始,先简单说下这个系列要用到的一些技术吧。同时也是对移动端重构一些技术的一个简单回顾。

    关于viewport详细请参考移动前端开发之viewport的深入理解

    css3选择器

    CSS3 选择器——属性选择器

    css选择器支持一览表

    伪元素(::before, ::after)

    A Whole Bunch of Amazing Stuff Pseudo Elements Can Do

    伪元素的content使用

    据说百分之八十的人入门移动端重构的第一个问题就会问:是不是所有的当要用百分比单位啊。这可以从侧面可以反应出百分比有多重要,下面是关于

    新单位——rem,vw,vh...

    CSS3的REM设置字体大小

    vw, vh等新单位介绍(安卓4.4+支持)

    flex

    一个完整的Flexbox指南

    A Complete Guide to Flexbox

    用一首来说就是”眼前的黑不是黑,你说的1px是什么1px“,下面就是各种奇淫技巧实现:

    Retina屏的移动设备如何实现真正1px的线?

    // retina border// 0.5px实现 ios9@mixin retina-one-px() {

    @media only screen and (-webkit-min-device-pixel-ratio: 2), screen and (-webkit-min-device-pixel-ratio: 3) {

    }

    }

    除了曾经的1px不再是1px,曾经的fixed也不再是我们熟悉的fixed了,再搞下去都要得fixed恐惧症了。

    Web移动端Fixed布局的解决方案

    我们现在一般android采用fixed布局;ios采用absolute,然后中间滚动使用-webkit-overflow-scrolling: touch;。如果还不行就具体问题具体分析。

    跟pc的不一样,移动端的图片很多都不是固定的宽高的(icon图标与头像等一些小图还是固定大小的),所以就面临一个问题:不能设置一个具体的高度,于是就会出现加载过程其他内容随着图片的加载慢慢向下移动。

    给图片提供一个容器,设置高度为0,根据宽度按照图片的比例使用paddin-top得到一个高度值,然后图片绝对定位设置宽高为100%即可,如图片尺寸为200*100(则高度为宽度的二分之一):

    }

    position: absolute; top: 0; left: 0; width: 100%; height: 100%;

    css中如何做到容器按比例缩放

    居中,居中,还是居中,重要的话说三次!!!

    Centering in CSS: A Complete Guide

    等分

    目前等分大概分为三种:

    1. 间距为固定值如10px,剩余宽度item等分

    2. 这次我将会在这个实战系列中把这三种情况一一剖析。

      这年头不会一两招css3动画,都不好意思说自己会css了。

      Advanced CSS3 2D and 3D Transform Techniques

      css3 animation 101

      css3动画疑难杂症一览

      这个line list的名字是我自己起的(大概的意思是单行列表),要实现的东西为sheral的line list,对应的scss组件为_line-list.scss,下图为line-list的一个缩影:

      这个UI应该是每个移动端网页都必备的,而且使用场景也是非常的丰富,所以这里我们采用一步步循序渐进的方式去重构。

      retina 1px

    3. 整行点击

    4. 如何方便扩展

    5. html结构

      结构方面,标签可以是ul.line-list>.line-item或者div.line-list>a.line-item,前者用于单页应用,后者用于链接跳转。

      .line-item { @extend %bar-line;}.line-list { background: #fff;

      + .line-list { margin-top: 10px;

      }

      由于这种line item的样式使用场景较多,所以我们封装了一个%bar-line,定义在sandal的_mixin.scss文件中(下面如无特殊说明,mixin和%均在该文件定义),如下:

      padding: 5px 10px;

      display: block;

      @if $activeStateSwitch{ //是否开启:active样式

      &:hover { background-color: darken($colorF, 3%);

      }

      content: "";

      @include retina-one-px-border;

      }

      下面解读下上面的scss代码:

      • 把1px挂在除第一个元素之外的伪元素before上,而第一个最上面和最后一个最下面的1px将会在父元素上实现,那样中间line-item之间的1px就很容易扩展实现缩进。

      • 右箭头跳转模式

        item之间的1px缩进,最开始和最末位的不缩进

      • .line-list--indent { @extend %border-tb; // 添加最上和最下的1px,形成封闭

        .line-item::before { left: 10px; // 缩进10px

        }.line-list--after-v { // 右箭头通过after生成

        .line-item { padding-right: 30px;

        @extend %item-v-right;

        }

        PS:这里缩进用的伪元素before的1px left定位来实现的,看到过有些方法是设置item的border-bottom,然后设置item的margin-left: 10px,这种实现方法是错误的,因为点击的不是整行了(缺了margin left的10px),当然也可以内嵌一个inner元素设置inner元素的margin left,或空元素定位等

        // border top & bottom%border-tb { position: relative;

        @include retina-one-px-border(top);

        z-index: 1; // 第一个元素点击的时候防止active背景色遮盖了1px

        &::after { content: "";

        @include retina-one-px-border(bottom);

        }// item arrow, 右侧箭头跳转指向%item-v-right {

        @include v-arrow;

        position: absolute;

        top: 50%;

        transform: rotate(45deg) translate(0, -50%);

        box-sizing: border-box;

        }

        选择模式

        // 单选.line-list--select { .line-item { padding-right: 30px;

        &::after { // 伪元素生成对钩

        display: block;

        height: 8px;

        border-left: 2px solid currentColor;

        transform: rotate(-52deg) translate(0, -50%);

        position: absolute;

        top: 50%;

        margin-top: -4px;

        }

        }// 多选.line-list--multi-select { .active{ color: $primary;

        .icon-checkbox{ color: $primary;

        }

        复杂模式

        这里我们将采用flex,一行大概分为三栏:图标icon(固定宽度),中间内容(剩余宽度),右边操作或提示(switch,提示文字或数字,右箭头)。如果你要兼容的手机不支持flex,那也没关系,这个结构也足够你使用绝对定位或float布局了,完全不需要再更改结构。

        align-items: center;

        .item-icon, .item-img, .icon-switch, .remind-num, .item-append{ margin-right: 10px;

        flex: 1;

        width: 1%;

        } .icon-v-right { width: 30px;

        height: 30px;

        margin-left: -10px;

        } .remind-num { position: static;

        line-height: 1.5;

        }

        3.各种等分

        单行,不考虑间距等分

        .nav-list{ @include equal-flex(nav-item);}

        // flex等分@mixin equal-flex($children: li) {

        $childrenEle: li div p a span strong;

        #{$children} { flex: 1;

        width: 1%;

        } @else {

        .#{$children} { // 自动加.成class

        width: 1%;

        }

        参数部分可以是常用的li div p a span strong几个元素,也可以是class,会自动加.

        // table 等分@mixin equal-table($children: li) {

        table-layout: fixed;

        $childrenEle: li div p a span strong;

        #{$children} { display: table-cell;

        } @else {

        .#{$children} { display: table-cell;

        }

        间距相等,剩余item平分

        分为单行及多行情况,单行直接flex就好,而多行的flex老版本兼容不是很好,所以不建议使用,直接用原始的float。

        .equal--gap{ @include line-equal-gap($children: line-equal-item);}

        // line equal@mixin line-equal-gap($gap: 10px, $lr: true, $children: li) {

        @if $lr { // 左右边缘是否有gap

        padding-right: $gap;

        #{$children} { flex: 1;

        &:not(:first-of-type){ margin-left: $gap;

        }

        .#{$children} { flex: 1;

        &:not(:first-of-type){ margin-left: $gap;

        }

        }

        关于多行的可以参考sheral的card实现,这里以卡片2为例,关键代码如下:

        $cardFlexSwitch: false !default; // 默认使用float$cardGap: 10px !default; // 默认间距为10px$carLineNum: 2 !default; // 目前只支持2 或 3 等分.card-list { @if $cardFlexSwitch {

        flex-wrap: wrap;

        } @else {

        } .card-item { position: relative;

        @if not $cardFlexSwitch {

        } .item-img { width: 100%;

        } .item-tt { line-height: 30px;

        }

        }.card-list--gap{ padding-left: $cardGap / 2;

        .card-item{ margin-bottom: $cardGap;

        padding-right: $cardGap / 2;

        }

        PS:这里考虑到flex与float的无缝切换,所以flex思路同样设置宽度的n等分,而不是单行的那种margin方法。

        单行的demo为line equal的第二个。这里使用的另一个mixin: line-equal-item,其实现思路是通过flexjustify-content: space-between;进行变化使用。

        @mixin line-equal-item($lr: true, $children: li) {

        justify-content: space-between;

        @if $lr {

        &::after { content: "";

        }

        多行的话,跟上面的card实现差不多,具体的间隙计算公式可以参考item宽度固定,剩余间距等分实现方案探讨

        4.进入离开动画

        在sandal的_animation.scss中我们定义了fade-in/out, shrink-in/out, up-in/out, down-in/out, left-in/out, right-in/out六组基础动画,下面我们以fade-in/out为例说明如何使用:

        @include animation-fade-in;@include animation-fade-out;

        .fade-in, .fade-out { -webkit-animation-duration: 0.3s; animation-duration: 0.3s; -webkit-animation-fill-mode: both; animation-fill-mode: both;

        }@-webkit-keyframes fadeIn {

        0% { opacity: 0;

        100% { opacity: 1;

        }}@keyframes fadeIn {

        0% { opacity: 0;

        100% { opacity: 1;

        }}.fade-out { -webkit-animation-name: fadeOut; animation-name: fadeOut;

        0% { opacity: 1;

        100% { opacity: 0;

        0% { opacity: 1;

        100% { opacity: 0;

        当然为了扩展,mixin还定义了两个参数:animation-fade-in($className: fade, $from: 0)animation-fade-out($className: fade, $to: 0),第一个表示要用的class名字(会自动补上in/out),第二个表示opacity值(from为起始,to为结束)

        es6 封装动画进入离开类

        constructor({ele, className, inCallback, outCallback}) { this.ele = ele.nodeType === 1 ? ele : document.querySelector(ele); this.inClass = className + '-in'; // 加上in表示进入class

        this.inCallback = inCallback; // 进入动画结束后回调函数

        this.animationend = this.whichEndEvent(); // 使用animationend事件

        } // 进入

        this.ele.addEventListener(this.animationend, this.endBind);

        leave() { this.ele.classList.add(this.outClass); // animation动画结束之后,移除该class

        } // 动画结束事件处理函数

        eleClassList = ele.classList,

        isOut = eleClassList.contains(this.outClass); // 离开

        eleClassList.remove(this.inClass); this.inCallback && this.inCallback();

        eleClassList.remove(this.outClass); this.outCallback && this.outCallback();

        } // 判断end事件,可独立为一个基础功能

        el = document.createElement('div'); var animations = { "animation" : "animationend", "WebkitAnimation": "webkitAnimationEnd"

        } for(k in animations) { if(el.style[k] !== undefined) { return animations[k];

        }

        }

        PS:注意这里我们采用的animation动画,而不是transition动画,因为transition动画从none到block的时候,直接添加动画的class是不会有动画效果的(除非使用回调函数或promise),而animation动画从none到block的时候添加动画class是可以的。这里不想设计得太复杂,所以直接使用animation动画

        function leaveEnd() { console.log('hello the world');

        PS:本系列教程未完待续,正在码字中...

        在后台回复福利,可以获取下载《全球移动技术大会2016》PPT集合。

        关注我们

        扫二维码 或搜索 fed-talk ,关注我们的微信公众号,也欢迎你将它分享给自己的朋友。

打赏
0相关评论
热门搜索排行
精彩图片
友情链接
声明:本站信息均由用户注册后自行发布,本站不承担任何法律责任。如有侵权请告知立立即做删除处理。
违法不良信息举报邮箱:115904045
头条快讯网 版权所有
中国互联网举报中心