标签: bootstrap

  • Bootstrap 发布图表库 Bootstrap Icons 1.0,该准备切换到 SVG 了

    Bootstrap 发布图表库 Bootstrap Icons 1.0,该准备切换到 SVG 了

    Bootstrap 团队一边开发 Bootstrap 5,一边发布了 Bootstrap Icons 1.0(以下简称 BI)。官方全文见此:Bootstrap Icons v1.0.0。这个版本里包含 1100 个图标,涵盖范围很广,全免费(MIT),可以自由使用、修改。

    真正引起我注意的是,这个仓库不再使用 Webfont,而是全部使用 SVG。官方文档 介绍了三种使用方式:

    1. 直接嵌入 SVG 代码
    2. <svg> 中引用 BI 库并配合 <use> 使用
    3. 通过 bootstrap-icons/icons/*.svg 直接使用

    如果你非常怀念 Webfont 方式,可以用(3)结合 CSS background-image 的方式近似的模拟。

    其实,GitHub 很早以前就开始从 Webfont(Icon font) 向 SVG 切换了,他们还特地写了篇博客解释这件事情:Delivering Octicons with SVG,在里面列举了六个理由:

    1. Icon font 本身存在一些渲染问题,Webkit 内核的浏览器会使其边缘模糊,不利于辨识
    2. Icon font 一般会延后加载,而字体在完成加载之前无法判定大小,所以加载完成之后,需要重新计算布局并且重新进行渲染,造成不必要的性能损耗
    3. 可用性。参考这篇 Slide:Death to Icon Fonts,每十个人当中就有一个人存在阅读障碍,他们很难使用普通字体,必须使用专为他们设计的特殊字体,而这个时候,icon font 就会变成没有意义的方块。对使用阅读器的人更甚:icon font 是一种 hack,通常来说,我们并不会真的写文字进去,所以在他们看起来,图标会变得完全不可读。
    4. 合适的图标尺寸
    5. 方便重构字体文件
    6. 方便制作动画

    所以,如今 Bootstrap 放弃 webfont,使用 SVG 作为主要格式,应该说是大势所趋。这几年使用 Webfont,的确很方便,给开发带来很大的效率提升,但与此同时,也存在不少问题:

    1. fontawesome 体积越来越大,现在一组字体要 1M+,加载很耗时,但是拆分非常困难,即使只用几个图标,也需要加载整个字体库
    2. 无法对图标进行稍微复杂的操作,比如双色,或者特定部位的动画
    3. 放大之后,图标比较丑,也不好加工
    4. 前面说过的那些问题

    所以未来我和我厂也要慢慢从 webfont 切回 SVG。这样也有助于提升我厂产品的可用性。其实,互联网行业的可用性一直都做得不错,软件开发目前也算是残障人士最有机会获得体面收入的机会,所以,我辈仍需努力呀。

  • 给 Bootstrap Modal 增加缩放功能

    给 Bootstrap Modal 增加缩放功能

    需求

    Bootstrap 应该还是目前最流行的前端基础框架之一。因为架构方面的优势,它的侵入性很低,可以以各种方式集成到其它项目当中。在我厂的各种产品里,都有它的用武之地。

    前两天,老板抱怨,说 Modal(弹窗)在他的屏幕上太小,浪费他的 5K 显示器。

    我看了一下,按照 Bootstrap 的设计,超过 1200px 就算是 XL,此时 .modal-lg 的宽度固定在 1140px。其实 Bootstrap 这么设计也有它的道理,因为人眼聚焦后宽度有限,如果弹窗太宽的话,内容一眼看不全,也不好。不过在我厂的产品里,弹窗要呈现火焰图,所以宽一些也有好处。

    技术方案

    那么,综合来看,最合适的做法,就给 Modal 添加一个拖拽的功能:用户觉得够大了,就这么着;用户想看大一点,就自己拉大一些,然后我记录用户的选择,以便复用。

    看过我《用 `resize` 和 MutationObserver 实现缩放 DOM 并记录尺寸》的同学,应该知道 resize 这个 CSS 属性,使用它可以很方便的给元素添加缩放功能。参考 caniuse 上面的普及度,大部分新版本的浏览器都已经支持,可以放心使用。

    使用它的时候要注意两点:

    首先,我们在缩放元素的同时,也会对它的子元素、父元素同时造成影响。因为在静态文档流当中,块级元素的宽度默认是父元素 content-box 的 100%,而高度由子元素决定。所以,对一个块级元素的缩放,不可能宽过它的父元素(如果限制了宽度的话),也不可能矮于它的子元素。

    其次,拖拽手柄的显示优先级很低,会被子元素盖住,哪怕子元素没有填充任何内容。换言之,一定要有 padding 的元素才适合添加 resize 缩放。

    实施方案

    总而言之,把这个属性加在哪个元素上面,很有讲究。具体到本次需求,Bootstrap Modal,最合适添加 resize 属性的的是 modal-content,因为它有 1rem 的内边距。

    但是限制宽度的是父元素,也就是 modal-dialog,它是响应式的,会根据显示器的宽度设置一个最大宽度。如果不修改它的 max-widthmodal-content 的最大宽度就无法超过它,达不到预期效果。但是也不能改成 width,这样的话,弹窗会失去弹性,分辨率低的时候表现不好。

    所以还是要在 max-width 上做文章。如果直接去掉它,modal-dialog 的宽度就会是 100%,失去弹窗效果,所以也不能这样做。最终,我的方案是:

    1. 窗口完全展开后,获取宽高,赋给 modal-content
    2. 去掉 modal-dialogmax-width
    3. 用 MutationObserver 监测 modal-content 的宽高,保存 localStorage,以便接下来使用

    完整代码展示

    我厂的产品基于 Vue 开发,所以部分逻辑用 Vue 组件实现。

    效果演示

    为方便在 Codepen 里呈现,有部分修改。

    代码及解释

    <template lang="pug">
    .modal.simple-modal(
      :style="{display: visibility ? 'block' : 'none'}",
      @click="doCloseFromBackdrop",
    )
      .modal-dialog.modal-dialog-scrollable(
        ref="dialog",
        :class="dialogClass",
      )
        .modal-content(ref="content", :style="contentStyle")
          .modal-header.p-2
            slot(name="header")
              h4 {{title}}
            span.close(v-if="canClose", @click="doClose") ×
          .modal-body
            slot(name="body")
    </template>
    
    <script>
    import debounce from 'lodash/debounce';
    
    const RESIZED_SIZE = 'resized_width_key';
    let sharedSize = null;
    
    export default {
      props: {
        canClose: {
          type: Boolean,
          default: true,
        },
        size: {
          type: String,
          default: null,
          validator: function(value) {
            return ['sm', 'lg', 'xl'].indexOf(value) !== -1;
          },
        },
        resizable: {
          type: Boolean,
          default: false,
        },
        backdrop: {
          type: Boolean,
          default: true,
        },
        title: {
          type: String,
          default: 'Modal title',
        },
      },
    
      computed: {
        dialogClass() {
          const classes = [];
          if (this.size) {
            classes.push(`modal-${this.size}`);
          }
          if (this.resizable) {
            classes.push('modal-dialog-resizable');
          }
          if (this.resizedSize) {
            classes.push('ready');
          }
          return classes.join(' ');
        },
        contentStyle() {
          if (!this.resizable || !this.resizedSize) {
            return null;
          }
          const {width, height} = this.resizedSize;
          return {
            width: `${width}px`,
            height: `${height}px`,
          };
        },
      },
    
      data() {
        return {
          visibility: false,
          resizedSize: null,
        };
      },
    
      methods: {
        async doOpen() {
          this.visibility = true;
          this.$emit('open');
          if (this.resizable) {
            // 通过 debounce 节流可以降低函数运行次数
            const onResize = debounce(this.onEditorResize, 100);
            // 这里用 MutationObserver 监测元素尺寸
            const observer = this.observer = new MutationObserver(onResize);
            observer.observe(this.$refs.content, {
              attributes: true,
            });
    
            if (sharedSize) {
              this.resizedSize = sharedSize;
            } 
            // 第一次运行的时候,记录 Modal 尺寸,避免太大
            if (!this.resizedSize) {
              await this.$nextTick();
              // 按照张鑫旭的说法,这里用 `clientWidth` 有性能问题,不过暂时还没有更好的解决方案
              // https://weibo.com/1263362863/ImwIOmamC
              const width = this.$refs.dialog.clientWidth;
              this.resizedSize = {width};
              // 这里产生纪录之后,上面的 computed 属性就会把 `max-width` 去掉了
            }
          }
        },
        doClose() {
          this.visibility = false;
          this.$emit('close');
        },
        doCloseFromBackdrop({target}) {
          if (!this.backdrop || target !== this.$el) {
            return;
          }
          this.doClose();
        },
    
        onEditorResize([{target}]) {
          const width = target.clientWidth;
          const height = target.clientHeight;
          if (width < 320 || height < 160) {
            return;
          }
          sharedSize = {width, height};
          localStorage.setItem(RESIZED_SIZE, JSON.stringify(sharedSize));
        },
      },
    
      beforeMount() {
        const size = localStorage.getItem(RESIZED_SIZE);
        if (size) {
          this.resizedSize = JSON.parse(size);
        }
      },
    
      beforeDestroy() {
        if (this.observer) {
          this.observer.disconnect();
          this.observer = null;
        }
      },
    };
    </script>
    
    <style lang="stylus">
    .simple-modal
      background-color: rgba(0, 0, 0, 0.5)
      .modal-content
        padding 1em
    
        .close
          cursor pointer
    
    .modal-dialog-resizable
      &.ready
        max-width unset !important
    
      .modal-content
        resize both
        margin 0 auto
    </style>

    注意

    因为浏览器的异步加载机制,有可能在 modal 打开并完成布局后,高度和宽度被内容撑开导致记录不准,或者内容被异常遮盖。请读者自己想办法处理,就当练习题吧。

    总结

    本次组件开发非常符合我理想的组件模式:

    1. 充分利用浏览器原生机制
    2. 配合尽量少的 JS
    3. 需要什么功能就加什么功能,不需要大而全

    在 MVVM 框架的配合下,这样的方案很容易实现。另一方面,每个项目都有独特的使用场景,通过长期在特定场景下工作,我们可以逐步整理出适用于这个场景的组件库,不断改进该项目的开发效率。我认为这才是组件化的正道。

  • Bootstrap 4 发布 alpha 6

    Bootstrap 4 发布 alpha 6

    这两天直播写 Meart 有点上瘾,博客好久没更了,公更私更都没更。刷推看到这则公告,小更一下吧。
    图文无关,马上又要去台湾了,翻出来一张台湾公安局的照片阵楼。

    Bootsrap 团队宣布 Bootstrap 4 推出 alpha 6 版本

    如题。Bootstrap 4 alpha 已经是第六个版本了,我最近一直在用 alpha 5,感觉没有太大问题。

    这次更新主要包含三个部分:

    1. 拥抱 Flexbox

    这个版本放弃了 IE9,从而可以彻底拥抱 Flexbox(之前的版本中,Grid 多半使用 display:tablefloat 实现,如果要启用 Flexbox,需要单独引用一个 bootstrap-flex.css)。

    根据 CanIuse 显示,目前除了 IE 系列,大部分浏览器均已全面支持 Flexbox。

    这次修改之后,绝大部分组件均已全面转向 Flexbox。

    2. 加强布局和显示样式

    增加了一批用于调整布局和显示的 class,这样从 Bootstrap 3 迁移也会方便一些。

    这块儿我暂时没用到,没啥可说的。

    3. 提升 Grid 布局

    增加了更多的可控样式,可以调整 Grid 布局中的边距。

    4. 升级导航栏

    之前的版本中,导航栏还是个半成品,现在虽然不敢说是“完成版”,单也差不多了。

    这个用途可能比较大,不过还没研究。


    下次再发布就是 beta 版了,感觉离正式推出不远了。

    Staticfile.org 搜索仓库还没更新,文件已经更新了。


    感谢原作者和贡献者的努力,让我们得以用上越来越好用的开源框架,节省大量时间和精力,可以集中于更有产出的事情。


    原文:Bootstrap 4 Alpha 6

  • Bootstrap 4 alpha 4 发布,v3 停止维护

    Bootstrap 4 alpha 4 发布,v3 停止维护

    业内广泛使用的前端框架 Bootstrap 发布了最新的 “v4 alpha 4” 版本,同时宣布关闭 v3 所有的问题和 pull request,以后将更专注于推进 v4 的发布。

    作者是这么说的:

    Bootstrap 3 都发布3年多了;4 也发布一年多了。老子还有全职工作要干,真心没工夫维护两个版本。

    感谢开源作者为我们贡献了如此优秀的框架,帮助我们节省大量的时间。v4 我也试用过,的确很好,有很多好用的新组件,整个 CSS 都用 Sass 重写了,结构也简洁很多。

    奉上 v4 文档地址:

    Bootstrap 4 alpha 4 文档


    来源

    Bootstrap 4 Alpha 4

  • 近期要学习实践的新技术

    近期要学习实践的新技术

    众所周知,程序员是一个必须随时更新知识技能的职业。这其中,前端程序员尤甚。虽然我自称全栈,多半也要依赖前端技术作为小无相功驱使内力,方能打出少林寺七十二绝技写出其它平台的产品。所以,主动学习,更新知识技能是我必须坚持的。

    同时,在前端这个充满变革的领域,我不能把各种新奇的东西都放到实际生产中去。所以,不定期的集中学习就不可避免。

    那么,近期要学习实践的新技术有哪些呢?

    1. Bootstrap 4
    2. gulp
    3. Angular 2 直接从2开始吧,计划做个Todo
    4. React + React Native 要不做鸡血君?
    5. Polymer
    6. Material Design Lite
    7. Phonegap 5 自然是迁移游戏泡泡
    8. Eletron 结合别的技术做个写博客的工具。
    9. socket.io
    10. browserify

    希望能在年底前搞定。

  • 新个人首页

    新个人首页

    最近比较忙,为了排遣压力,把我的首页重新整了一下:点我访问

    感谢各种开源组织和个人,感谢github提供静态托管服务,感谢点乐让我成长起来,可以随手一搞就完成这个项目。

    (更多…)

  • 推荐好用的JS CDN

    上周GFW又抽风,导致取自CDN的jQuery和Bootstrap经常404,后台各种罢工。

    开始想说干脆放弃CDN得了,结果自家服务器也不是很给力……本地路径的静态文件也经常加载失败,挠了半天头,再去找找国内的CDN吧。

    后来想起来前几天看到七牛搞了个免费的开源仓库CDN,通过Google找到,叫http://www.staticfile.org/。打开一看,首页只列出不多的几个库,版本也不是最新的。我以为又是个没人维护的烂尾工程,读了介绍才知道他们倡导大家都来提交库信息,共同建立全面的CDN资源。我本想把这次要到的库和可以更新的库提交上去,后来发现原来他们已经引入了cdnjs.com里所有的库,只不过没有写在首页……果然大家都喜欢写代码不喜欢写文档啊,差点就错过了。

    BTW,cdnjs.com居然还提供了animate.css,真好。

    好在他们做了命令行工具,可以装上查引用地址。比如我想知道能不能用underscore,就可以这样:

    // 安装
    npm install -g sfile
    
    // 查找underscore
    sfile search underscore
    
    // 得到链接,这里要用全名
    sfile get underscore.js

    最后看https://github.com/staticfile/static/issues里的内容才知道,他们会把国外成熟的库直接从cdnjs复制过来,提交新库应以国内的为主。嗯,将来把Nervenet弄完也提交进去。

    重复一下网站地址:http://www.staticfile.org/。最后感谢下这些好人,以及服务提供商七牛云存储

  • 前端框架点评

    前两天被人@,提问前端框架的问题。这还是第一次在微博上被陌生人提问,感觉有点小激动。今天写篇小文章,简单点评一下我用过的前端框架,希望对大家有所帮助。

    本文涉及的范围

    本文要点评的前端框架,都是我用过的,以JavaScript框架为主,也可能包括一些工具类库。由于前端的特殊性,弱语言HTML和CSS是必不可少的工具,运行机制导致很多成熟的工程学方法没法用,必须用工具库补充。烦请大家不要抠字眼,我一向很马虎,呵呵。

    Bootstrap

    一定要先推荐Bootstrap。它由Twitter的两位工程师首创,当前版本2.3.1,涵盖了文本、布局、表单、工具,除了暂时没有日期选择器之外,几乎包含了网页所需的一切。我们知道,网页出现是为了传递信息,而成熟的印刷业对其影响很大,表现出来就是,HTML中很多标签,比如H1~H6p等,都是有具体语义的。但很多前端框架并未给予他们足够的重视,所以往往需要我们重置或者自己动手写。Bootstrap则不是这样。它不仅包含大量组件,还帮助我们给每种可能用到的语义标签都定义了样式,又设计出很多helper;而其网格系统也能帮我们出色完成排版工作。总而言之,使用它几乎足以满足任何网页开发需求。

    值得一提的是,Bootstrap的组件和Widget都基于HTML标签构建,非常好用;而响应用户操作则基于对document的侦听,所以几乎不会对我们其它代码逻辑造成影响,我们可以放心大胆的在项目当中使用。当然,Bootstrap依赖jQuery,后者近期遭遇到一些纷争。不过我还是要强烈推荐这个框架。

    jQuery UI

    比较遗憾的是,虽然号称UI,但jQuery UI只能算作一个组件库。因为缺少基础排版支持,所以既想使用jQuery UI,又想和自己的样式保持一致会有些困难,需要花费不少时间做修改。

    抛开这一点,jQuery UI也是个很好的工具,几个widget都很实用,文档丰富详实,如果项目本身对交互要求不高,只是个别地方需要一些功能补充,jQuery UI确实是不错的选择。尤其当我们需要draggablesortableresizable这几个功能时,选择的余地真的不大。

    HTML5 Boilerplate

    这个工具力图给开发者提供一个舒适的起点。他们为项目建立了标准化的目录结构,并且把常用的资源都整理在合适的位置,这样我们只需要替换它们就能保证项目对所有浏览器都有完美表现。同时,他们重置了CSS,引入了GA统计,在页面中标记了合适的输入点,这样我们能尽快投入到项目逻辑的开发中。

    但这个工具的问题也很明显,它努力达成的目标,是消弭浏览期差异。假如我们只需要统一的环境就能开展工作,那H5B就足够了;而实际上,我们通常需要更多,比如排版、比如组件。所以,这个工具对我来说,学习的意义大于使用的意义。

    HTML KickStart

    我见过的前端框架里只有这个跟Bootstrap的覆盖面接近。它的设计更好,更鲜艳更有活力,不过最近的开发好像慢了下来。我并没有真正使用它,因为它的组件太少了。

    Backbone

    非常好的MV*框架,真的像根脊柱一样,把被jQuery拆的七零八散的js统一在合理的框架内,让整个项目都变得协调了。引入Collection真的很天才,结合其出色的View设计——其实Backbone的View并不是传统意义上的View,更像mediator——可以看出,Backbone的设计者对Web开发有着深刻理解。哦,差点忘了,Router还能帮我们很好的整理单页应用的逻辑。

    不过js毕竟是一门很烂的语言——或者让我换种表述方式,缺陷很多,所以它尚无法做到像AS3的Robotlegs那样好。不过,如果我们希望把代码组织的更好,Backbone类的框架是不可或缺的。

    Rachet

    Rachet的目的和架构,很像Bootstrap,不过它瞄准在移动端。Rachet现在仍处于比较低级的阶段,组件太少,开发起来不太方便,最多相当于Kickstart把。可惜的是,移动端移动框架普遍较弱,在我看来,基于它的Junior仍然是不二之选。

    Zepto

    说到移动端开发就不能不提Zepto。它力图实现一个十分类似jQuery的库,不过只考虑支持移动端,所以体积会小很多,速度也会快不少。Zepto的升级频率最近也不高,不过还是到了1.0。新版Zepto放弃了ruby,使用coffee作为编译工具,支持使用者自由组合功能模块,相当贴心。

    不过具体使用时,还是会有一些函数的用法和jQuery不同,需要注意。举个例子,css函数,jQuery里支持数字,比如$('div').css('height', 100),会把页面中所有div的高度设置为100px;而Zepto不行,必须$('div').css('height', '100px')才会正常工作。

    jQuery Mobile

    强大的jQuery团队最失败的作品。贪恋于风光的过去,迷信“全覆盖”,使得jQuery Mobile步履蹒跚,在移动平台上无法取得足够好的性能表现(早期版本甚至不支持固定底部)。而这直接带来了对HTML在移动端的质疑。有很多本不擅长前端开发的人,看到Phonegap,就直接上jQuery Mobile,结果发现性能不行,体验很差,便在各种渠道大放厥词,说HTML不行。这种行为直接伤害了HTML和Hybrid应用。

    jQuery Mobile失败不仅在于此。jQuery本身方便快捷,使用它很容易养成不重视代码组织的习惯;而在移动端,单页应用占据主流,这意味着,使用jQuery Mobile很可能无法良好的组织代码,直接带来项目维护成本的提高。另外,早期版本的设计也不可避免的带有大量Web痕迹,导致实用性不足。所以直到今天,jQuery Mobile都还是个悲剧。

    不过,jQuery 2.0已经beta2了,我们知道,jQuery 2.0里放弃了对IE678的支持,性能会得到不小的提升;jQuery Mobile 1.3已经引入了对hash的支持,组织代码好多了;同时,随着3次小版本更新,越来越多适用于移动应用的组件加入进来,大大扩充了军备库。我认为,未来的jQuery Mobile,值得一试。

    Sencha Touch

    大家可能还记得,Facebook闹过一阵,说HTML5不够好,还把他们的手机客户端用原生重做了一遍。之后Sencha的两位工程师表示不服,以HTML5为基础开发了功能几乎完全一样的客户端,他们用到的框架就是Sencha Touch。据说,它的性能比jQuery Mobile好不少。

    但是,真正用了就知道,sencha touch实在太不“前端”了。它里面几乎没有给HTML和CSS留下太多位置,明明十分强力的布局样式工具,被封装成了单薄的接口;但是没少往JS框架里加内容。结果就是用起来非常复杂,随时需要看文档;自定义困难,它里面甚至有一个属性叫“html”,用来填入大段文本类型的html;数级嵌套起来的对象也很让人头疼,而且感觉维护也不会太容易。所以我做了一半就还是放弃了。劝大家也别去尝试了。

    如果有哪位高人愿意以实际经验教育我的,我洗耳恭听。

  • 一个诡异问题的排查

    这个问题本身其实不能称之为问题,记下来只是我觉得排查经历挺典型的,值得分享。

    先说下我的环境:MacOS 10.8.2 + Chrome 23.x + Bootstrap 2.2.1 + jQuery 1.8.2。事情是这样的,我本来在开发一个新功能,顺便把老页面重构一下,貌似一切正常。重构完毕后,我想随手测试一下重构后的页面,发现下拉菜单竟然不能点击了,或者说,点击完全失效了。

    这让我很恐慌,因为类似的重构过程我近段时间来做的不是一次两次,这次我应该没有任何冒险的改动才是。这种情况下出现的Bug,一般都很棘手。于是,我打开以前的某个页面——同样经历了重构,但是通过了测试,并且在一顿时间的使用中完全没有问题——发现里面的下拉菜单同样点击失效了。于是,真正的排查开始了。

    (更多…)

  • 使用免费CDN加速JavaScript的加载

    做前端的,基本都会用上各种开源的框架、类库。在项目中引用它们,就可能需要把用到的文件纳入版本库,这样每次类库更新,我们也得跟着更新文件,比较麻烦。而且,我们知道,浏览器对同一个域名下的资源同时加载的数量是有限制的,加载页面时光类库的CSS和JS就得搞半天,整体速度也会被拖累。

    好在很多仁人志士已经提供免费的CDN分流服务,帮助我们托管类库。我用到的主要有这些:

    jQuery

    jQuery流行度毋庸置疑,所以微软和Google都提供了jQuery系列,包括UI和Mobile的CDN,Google的CDN还包括一些其它类库,比如prototype和Mootools等。

    微软:http://www.asp.net/ajaxlibrary/cdn.ashx#jQuery_Releases_on_the_CDN_0

    Google:https://developers.google.com/speed/libraries/devguide

    Bootstrap

    Bootstrap是由来自twitter的两名“Nerd”工程师创建的前端框架,非常棒,刚好覆盖了网站页面所需,组合使用无往不利。BootstrapCDN为我们提供了相应的服务:

    http://www.bootstrapcdn.com/

    Underscore和Backbone

    cdnjs.com 这种业界良心提供了最丰富的CDN资源,还包括了每种类库的官网或者其在github上的链接(好吧,很多时候他们就是一回事儿),我觉得甚至可以把这个站点当成学习的出发点。肉大师项目中用到的Underscore和Backbone就取自这里,感谢他们。

    值得一提的是他们的域名也很好记~