移动前端重构实战系列:5-7章
zhezhongyun 2025-08-06 23:56 3 浏览
(本文系来自腾讯imweb团队 结一大大 关于移动端重构经验以及思想的实战系列,推荐点击左下角的阅读原文。)
”本系列教程为实战教程,是自己移动端重构经验及思想的一次总结,也是对sheral UI的一次全方位剖析,首发在imweb和w3cplus两大站点及“前端Talk”微信公众号,其余所有标注或没有标注来源的均为转载。“
——imweb 结一
5.Form
form
大概要实现的效果如下图(具体demo可见sheral form):
粗略一看,跟line list差不多,好像可以直接套用,但是深究起来还是有那么些不同的,大概有以下几点用户体验差别:
输入框可点击范围
右边的箭头可点击范围
line list设计95%都是整行点击,所以不管你点哪,都是触发整行的点击事件,右边的箭头就是个指引而已,所以伪元素生成是没有问题的;而form就不一样了,右边箭头是真的要挂载事件的,所以除了直接使用元素外,点击范围一定要设计合理,总不能箭头多大就多大,那操作起来就不方便了,同理input框我们也需要设计成整行的高度,方便点击输入。以电话为例,既可以手动在input框输入,也可以点击右边的箭头去通讯录选择,所以单纯的复制line list过来是行不通的,下面从html及scss代码简单分析下。
从html结构上,大体分为三列,分别为label,表单元素及右边附加部分
.form-item
label.item-label
.item-field
input:text.f-text <!-- 表单元素 -->
p.field-value.placehold <!-- 选择值或默认值或选择说明 -->
i.icon-v-right <!-- 右边部分 -->
而scss主要是flex布局,设置中间输入元素部分为flex:1;
.form-item{ display: flex; // flex布局,子元素垂直居中
align-items: center;
position: relative;
line-height: $barHeight;
overflow: hidden;
&:not(:first-of-type)::before { // 1px 分割线
content: "";
@include retina-one-px-border;
} .item-field{ // 剩余宽度
flex: 1;
width: 1%;
} .icon-v-right{ // 右侧箭头
display: block;
width: 30px;
height: $barHeight;
color: #ccc;
}
}
错误处理
主要提供了四个icon,分别为alert、info、question、ok,demo可在sheral icon查看,样式定义在sandal/ext/_icons.scss
中,代码如下:
.icon-alert{ color: $red;
&::after{ content: "!";
}
}.icon-info{ color: $blue;
&::after{ content: "i";
}
}.icon-question{ color: $blue;
&::after{ content: "?";
}
}.icon-ok{ color: $green;
&::after{ content: "";
position: absolute;
top: 50%;
left: 50%;
width: 5px;
height: 2px;
border-bottom: 1px solid currentColor;
border-left: 1px solid currentColor;
transform: translate(-50%, -50%) rotate(-52deg) scale(1.5);
margin-top: -1px;
}
}
其中alert、info、question三个icon里面的图标分别为!
、 i
、 ?
,通过伪元素after设置content,而ok的则通过伪元素after绘制,为了达到和前面三个一样的粗细,使用了transform的scale
search
demo见sheral search
主要说下第二个搜索框。它其实是个假的搜索输入框,点击跳转到真的搜索,所以居中的icon和文字其实并不是input的内容。
range
demo见 sheral range
直接使用了input:range
,所以样式方面主要是重置range的shadow dom样式,样式比较多,可直接参阅_range.scss或自行搜索相关文章。
对于某些低端浏览器兼容问题,包裹了一层.range-wrap
,通过包裹的伪元素生成range的trace
6.icon与图片
icon
对于icon的问题多数都集中在颜色和大小上,所以sheral采用了svg icon和css绘制的icon,关于svg icon网上已经有很多文章了,所以这里主要介绍绘制icon的一些技巧,如下以搜索图标为例:
// icon search.icon-search { position: relative;
&::before { content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 2px solid currentColor;
height: 12px;
width: 12px;
box-sizing: border-box;
border-radius: 50%;
margin-left: -2px;
margin-top: -2px;
}
&::after { content: "";
background: currentColor;
height: 6px;
width: 2px;
position: absolute;
top: 50%;
left: 50%;
margin-left: 4px;
margin-top: 4px;
transform: translate(-50%, -50%) rotate(-45deg);
}
}
icon-search本身没有设置大小,只充当了一个relative的容器
绘制的功能交给伪元素before和after
伪元素采用绝对定位居中
颜色使用currentColor
这样做可以带来两个好处,一是可以方便设置icon-search的大小(扩大点击范围同时,还保持水平垂直居中),二是可以方便修改颜色(设置icon-search的color即可更改颜色)
其他的一些绘制icon具体可见sandal/ext/_icon.scss
文件,demo可见sheral icon
图片
关于图片这里主要讨论三点:
普通图片
图片的宽高比
背景图片大小
1、对于第一点,在sandal的_reset.scss
中就已经重置好了
img{ vertical-align: middle;
max-width: 100%;}
2、对于图片的宽高比,我们在基础知识中已经说了下,这里再具体说明下如何使用,以card list为例。
如果你多刷新几次应该就可以看到卡片1与2的图片区别了,1的图片区域有了高度,而2没有,所以1图片的加载不会影响下面内容的变化,而2加载图片会把下面内容向下排挤。这是因为卡片1的图片我们包裹了一层,设置了一个宽高比,而卡片2没有。
下面详细说下它们之间的html和css区别
<!-- 卡片1 -->.item-img-wrap > img.item-img<!-- 卡片2-->img.item-img// 卡片1 .item-img-wrap{ @include object-wrap(100%, '.item-img');}// 卡片2.item-img{ width: 100%;}
其中mixin object-wrap在sandal中定义如下(具体解释可参阅css中如何做到容器按比例缩放):
// object wrap// $child 参数请使用单引号,因为用于子元素选择器@mixin object-wrap($percent: 100%, $child: 'img') {
position: relative;
padding-top: $percent;
height: 0;
#{unquote($child)} { position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
3、至于最后的背景图片,说起来又是个悲伤的故事,虽然css3的background-size
已经非常强大了,但是安卓强大的阵线中总有某些机子总是拖了一大截后腿的。
首先安卓4.3-不支持background-size
的缩写,这倒没什么,再另写一行就是了,关键是有些安卓4.3-还不支持百分比单位。于是只好把目光转向cover
或contain
了(更多介绍请参考background-size),这又涉及到容器的宽高了。
如果容器已经有了宽高(当然这里的宽高是指可以随着机型变化的),比如全屏,我们就直接用cover了;而如果容器没有宽高,那就又回到了第二个问题,我们可以使用图片或者把设置背景图的这个容器设置成我们图片的宽高比,那样再使用cover或contain都可以。
以微信的朋友圈头部的背景图片为例(这里只是进行分析,具体的实现技术我也不知道):
在更换相册封面的时候,对选择的图片进行了1:1的裁剪
按照第二种情况,设置背景图片的容器的高度等于宽度(图片是1:1),然后设置background-size为cover即可
或者按照第二种情况,使用img元素,外面再嵌套一层wrap设置高度
PS:默认看到的大概是图片下面的四分之三(我并没有去量尺寸,根据经验猜测而已,如有错误请见谅),上面的四分之一当我们向下拉取刷新的时候就可以看到整个图片了
7.环形UI
圆形进度条
具体demo效果可见:sheral progress
这里主要分析下圆环的实现,蓝色的进度条圆环由左右两边构成,这里以右半边的为例,html结构为.circle-right > .right-inner
(为了视觉效果,把蓝色放在里面了,而非覆盖在灰色上),如下图:
半圆环由两层结构构成,.cicle-right
的大小为50px100px,超过隐藏,而.right-inner
的实际大小为100px100px,因为父容器宽度为50px且超过隐藏所以只会显示右边一半,如果只是这样的话我们旋转.right-inner
的话右边一直会有蓝色的半环。所以我们对.right-inner
再做个裁剪处理clip: rect(0, 50px, 100px, 0);
,这样导致整个.right-inner
圆环只有左半边可见,然后加上我们父元素是在右半边且多余隐藏,所以默认看不到我们的蓝色圆环,如要看到蓝色圆环部分只有将.right-inner
左边部分旋转到右边父元素的范围内,核心代码如下图:
.circle-right{
width: 50px;
height: 100px;
position: absolute;
top: -4px;
right: -4px;
overflow: hidden;}.right-inner{ width: 100px;
height: 100px;
position: absolute;
right: 0;
top: 0;
border-radius: 50%;
border: 4px solid #007aff;
box-sizing: border-box;
clip: rect(0, 50px, 100px, 0); // 设置左半边可见
transition: transform 0.5s linear;}
同理即可实现左半边,不过如果某些安卓机比较卡的话,在大于50%的时候,左边半圆在衔接的时候会有一个卡顿不连贯,所以可以考虑增加再增加一个底部的半圆环,让其在右边半圆环运动到一半的时候开始运动,然后设置一个比较短的完成时间,这样就可以衔接起来。
当然其实使用svg更方便,这里推荐一个库progress bar.js
PS:关于svg path的弧形绘制可参考MDN svg 路径的最后弧形部分
弧形tool
要实现的效果如下图,具体demo可见sheral tool
这里主要涉及到两点:
旋转角度计算
图标再旋转回来处理
动画处理,主要对opacity和transform进行动画
如果按总的90deg角计算,index表示item的索引(从1开始),n表示item总数,则每个item的旋转角度计算公式为:
每个item的旋转角度 = ( index -1) / (n - 1) * 90deg
而图标就要相应的旋转对应的负角度回来,于是每个icon的旋转角度计算公式为:
每个图标的旋转角度 = -( index -1) / (n - 1) * 90deg
默认样式:
.tool-item{ width: $quickToolSize;
height: $quickToolSize;
position: absolute;
background: $quickToolBg;
border-radius: 50%;
opacity: 0;
transition: opacity .3s linear, transform .3s $timingFunction;
@include center-flex(both);}
active样式
.tool-item{ opacity: 1;
@for $i from 1 through $quickToolNum{
&:nth-of-type(#{$i}) { // item旋转 加 偏移
transform: rotate(($i - 1) * 90deg / ($quickToolNum - 1)) translateX(-80px);
// transition-delay: ($i - 1) * 0.03s;
.item-icon{ // icon旋转
transform: rotate(-($i - 1) * 90deg / ($quickToolNum - 1));
}
}
}
}
抽奖圆盘
转盘背景图如下图,我们要把奖品填写到相应的区域。
大体思想跟上面的差不多,大概如下:
1、先绝对定位在圆中间,然后先计算每个item的旋转角度,再设置偏移值
.item{ position: absolute;
left: 50%;
top: 50%;}@for $i from 1 through 8 {
.item:nth-child(#{$i}){ transform: translate(-50%, -50%) rotate(($i - 1)*45deg + 22.5deg) translate(90px, 0);
}
}
2、调整奖品文字的旋转
.item-inner{ transform: rotate(90deg);}
早读课提醒
言归正传我们在微信群中推出了《早读课》,每日分享一篇我们认真精选的文章(不限于前端开发类),其目的是帮助开发者来学习有价值的东西。想加微信群的朋友,直接添加我的微信号:icepy_1988,审核之后会邀请你入群。想加QQ群的朋友,可以直接添加:418898836,答对问题即可入群。
关注我们
扫二维码 或搜索 fed-talk ,关注我们的微信公众号,也欢迎你将它分享给自己的朋友。
相关推荐
- Chinese vice premier calls for multilateralism at Davos
-
DAVOS,Switzerland,Jan.21(Xinhua)--ChineseVicePremierDingXuexiangdeliveredaspeechatthe...
- 用C++ Qt手把手打造炫酷汽车仪表盘
-
一、项目背景与核心价值在车载HMI(人机交互界面)开发领域,虚拟仪表盘是智能座舱的核心组件。本项目基于C++Qt框架实现一个具备专业级效果的时速表模块,涵盖以下技术要点:Qt图形绘制核心机制(QPa...
- 系列专栏(八):JS的第七种基本类型Symbols
-
ES6作为新一代JavaScript标准,已正式与广大前端开发者见面。为了让大家对ES6的诸多新特性有更深入的了解,MozillaWeb开发者博客推出了《ES6InDepth》系列文章。CSDN...
- MFC界面开发工具BCG v31.1 - 增强功能区、工具箱功能
-
点击“了解更多”获取工具亲爱的BCGSoft用户,我们非常高兴地宣布BCGControlBarProfessionalforMFC和BCGSuiteforMFCv31.2正式发布!新版本支...
- 雅居乐上调出售吉隆坡项目保留金,预计亏损扩大至6.64亿元
-
1月2日,雅居乐集团(03383.HK)发布有关出售一家附属公司股权披露交易的补充公告。此前雅居乐集团曾公告,2023年11月8日(交易时段后),集团子公司AgileRealEstateDeve...
- Full text: Address by Vice Premier Ding Xuexiang's at World Economic Forum Annual Meeting 2025
-
DAVOS,Switzerland,Jan.21(Xinhua)--ChineseVicePremierDingXuexiangonTuesdaydeliveredasp...
- 手机性能好不好 GPU玄学曲线告诉你
-
前言各位在看测试者对手机进行评测时或许会见过“安卓玄学曲线”,所谓中的安卓玄学曲线真名为“ProfileGPURendering”。大多数情况下,在系统“开发者选项中被称为“GPU显示配置文件”或...
- 小迈科技 X Hologres:高可用的百亿级广告实时数仓建设
-
通过本文,我们将会介绍小迈科技如何通过Hologres搭建高可用的实时数仓。一、业务介绍小迈科技成立于2015年1月,是一家致力以数字化领先为优势,实现业务高质量自增长的移动互联网科技公司。始...
- vue3新特征和所有的属性,方法汇总及其对应源码分析
-
vue3新特征汇总与源码分析(备注:vue3使用typescript编写)何为应用?constapp=Vue.createApp({})app就是一个应用。应用的配置和应用的API就是app应用...
- China's stability redefines global trade in a volatile era
-
ContainersareunloadedatQingdaoPort,eastChina'sShandongProvince,December10,2024.[Photo/X...
- QML 实现图片帧渐隐渐显轮播
-
前言所谓图片帧渐隐渐显轮播就是,一组图片列表,当前图片逐渐改变透明度隐藏,同时下一张图片逐渐改变透明度显示,依次循环,达到渐隐渐显的效果,该效果常用于图片展示,相比左右自动切换的轮播方式来说,这种方式...
- 前端惊魂夜:我竟在CSS里写出了JavaScript?
-
凌晨两点,写字楼里只剩下我工位上的一盏孤灯。咖啡杯见底,屏幕的光映在疲惫的眼镜片上。为了实现一个极其复杂的动态渐变效果,我翻遍了MDN文档,试遍了所有已知的CSS技巧,却始终差那么一口气。“要是CSS...
- 10 个派上用场的 Flutter 小部件
-
尝试学习一门新语言可能会令人恐惧和厌烦。很多时候,我们希望我们知道早先存在的某些功能。在今天的文章中,我将告诉你我希望早点知道的最方便的颤振小部件。SpacerSpacer创建一个可调整的空白空...
- 让我的 Flutter 代码整洁 10 倍的 5 种
-
如果你曾在Flutter中使用过SingleTickerProviderStateMixin来制作动画,猜猜怎么着?你已经使用过Mixin了——恭喜你,你已经处于一段你甚至不知道的关...
- daisyUI - 主题漂亮、代码纯净!免费开源的 Tailwind CSS 组件库
-
漂亮有特色的CSS组件库,组件代码非常简洁,也支持深度定制主题、定制组件,可以搭配Vue/React等框架使用。关于daisyUIdaisyUI是一款极为流行的CSSUI组件库,...
- 一周热门
- 最近发表
-
- Chinese vice premier calls for multilateralism at Davos
- 用C++ Qt手把手打造炫酷汽车仪表盘
- 系列专栏(八):JS的第七种基本类型Symbols
- MFC界面开发工具BCG v31.1 - 增强功能区、工具箱功能
- 雅居乐上调出售吉隆坡项目保留金,预计亏损扩大至6.64亿元
- Full text: Address by Vice Premier Ding Xuexiang's at World Economic Forum Annual Meeting 2025
- 手机性能好不好 GPU玄学曲线告诉你
- 小迈科技 X Hologres:高可用的百亿级广告实时数仓建设
- vue3新特征和所有的属性,方法汇总及其对应源码分析
- China's stability redefines global trade in a volatile era
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- HTML button formtarget 属性 (30)
- opacity 属性 (32)
- transition 属性 (33)