lancer07

lancer07 查看完整档案

上海编辑复旦大学  |  计算机科学与技术 编辑360  |  前端开发工程师 编辑 www.linqing07.com 编辑
编辑

360的小前端一枚

个人动态

lancer07 提出了问题 · 2020-02-27

为什么在mac上启动flutter项目,打开模拟器调试后风扇就狂转?

求大佬解答~~~~

关注 4 回答 3

lancer07 赞了文章 · 2017-08-24

趣店前端团队基于 koajs 的前后端分离实践

前言

关于前后端分离,我的感觉其实也是:这么老土的话题,为什么还要拿出来老调重弹?

但越来越发现基于前后端分离的类RESTful架构,能很好的满足WebAPP的业务需求。尤其是WebAPP+NativeAPP产品为主的中小型公司,能让整个公司的服务端研发和部署更灵活。

PS:已经了解前后端分离和koajs(不喜欢看背景和扯淡)同学可以直接跳到:“三、如何实践”。

一、什么是前后端分离?

前后端分离的概念和优势在这里不再赘述,有兴趣的同学可以看各个前辈们一系列总结和讨论:

尤其是《前后端分离的思考与实践》系列文章非常全面的阐述了前后端分离的意义和技术实现。如果不想看上面的文章,可以在脑海里留下这样一个轮廓就好:

图片描述

本文主要阐述趣店团队基于Koajs的前后端分离实践方案。

二、为什么选择koa?

koa是由Express 原班人马打造的一个更小、更健壮、更富有表现力的 Web 框架。(koa官网需用梯子,中文文档可以参考郭宇大神的翻译:koa中文文档,这里也不详细介绍。)

图片描述

看到koa之后,简单看了下文档和源码,立刻感觉koa不就是为前后端分离而诞生的轻量级框架吗?

因为:

  • 如果要用koa实现一整套类似于PHP的Laravel或者Ruby on Rails的服务端MVC框架其实还有很长的路要走;

  • koa中间件的思路可以很好的被前后端分离的思路利用起来,比如:路由、模板引擎、数据代理等等;

  • koa基于ES6的generator特性(不了解generator的同学可以参考阮一峰老师的文章),利用co等模块让前端同学写一个路由再简单不过;

  • ……

总之,koa给我的第一印象就是:如果用它来实现一套前后端分离的框架,非常高效轻量易扩展

三、如何实践?

1、刀耕火种的年代

在谈如何实现基于koajs的前后端分离框架的之前,必须提一下在做分离之前趣店(原趣分期)的情况:

  • 一个仓储托管了趣分期业务前后端所有代码,由于不断维护,这个仓储足足有600M

  • 后端为了满足NativeAPP的接口需求,这个600M的仓储还包含了提供给客户端的代码

  • 前后端开发流程为:先由后端写一个路由 → 前端将这个路由写成可以交互的页面 → 前端提交代码后后端同学“套模板”填充数据

  • 前后端开发都在一台远程开发机上,通过samba隐射到本地进行开发;

  • 前端几乎没有做任何打包编译;有时候为了偷懒,甚至把CSS和JS直接以内嵌的方式写在模板里(不要去联想vue,两码事儿)

写到这儿想贴代码来着,想想还是算了……

2、基于koa的实现

无论使用什么工具要实现前后端分离框架,无非要满足这样几点:

  • 更便捷地创建路由

  • 更高效地代理数据请求

  • 更灵活地环境部署

先看一个已经实现了demo应用的目录结构:

.
└── demo
    ├── controller
    │   ├── data.js
    │   ├── defaultCtrl.js
    │   └── home.js
    ├── static
    │   ├── css
    │   ├── image
    │   └── js
    └── views
        └── home.html

这个结构大家再熟悉不过:controller提供路由;static包含静态文件;views提供模板文件。
这里可能大家会有疑问,如果我的前端构建是components的模式怎么办呢?

├──components_a
│   ├── home.js
│   ├── home.css
│   └── home.html
├──components_b
...

没关系,do what u want!很多boilerplate可以直接拿到这个框架下直接使用,我们团队自己也正在使用了requirejs和vue的构建方案。

1. 更便捷地创建路由:controller

回到上文提到的案例:

controller
├── data.js
└── home.js

这段代码生成的是一个/data/*,/home/*路由,用过Express的同学对这种路由实现肯定一点都不陌生。

但有一点不一样的是,koa下面每一个路由是一个独立的generator函数(很遗憾,generator函数不能使用箭头函数的语法来表示),参看controller/home.js

exports.index = function* () {
  yield this.bindDefault();
  yield this.render('home', {
    title: 'Hello , Grace!'
  });
}

你还可以用一个对象把一个controller包起来,,参看controller/data.js

exports.info = {
  repo: function*(){
    yield this.proxy('github:repos/xiongwilee/koa-grace')
  }
}

这种写法非常适用于仅仅用来做一个数据代理的ajax请求的控制器。当然,如果需要的话,你还可以在控制器里对数据做一些加工。

另外,还有几个细节与express的路由控制器不太一样:

  1. 终止请求的动作都有koa来完成,在这里的controller里,你不需要通过res.end()来终止请求;

  2. 这里的controller的作用域,就是koa的上下文,所以你可以通过this.req获取request对象;

  3. 这里的controller里,异步回调的语法成为了往事,请尽情的使用yield

我们对现在的路由方式做了一个压测:8核CPU/8G内存的机器,通过一个路由返回hello world!,结果显示:8核CPU/8G至少能扛住1000QPS的压力(cpu idle不到60%,cpu load接近8)。

2. 更高效地代理数据请求:proxy

前后端分离模式下的,数据代理主要有这两种使命:

  • 一种是,拼装一个和多个数据结果,给模板渲染数据或者给Ajax接口

  • 另外一种是,接受用户的数据请求,将结果交给一个后端接口

第一种场景,比如:一个用户中心的页面,需要展示用户的用户信息、订单列表、喜欢的商品推荐等等信息。由于后端的架构,用户、订单、商品可能都是属于不同的服务,这就意味着,后端同学需要给你同时拼装这么多数据,拼装还有可能是同步获取每个接口然后再一起返回给前端。

现在,就可以尽情的发挥nodejs异步并发及koa的generator同步语法的优势,我们还可以这么写:

 repo: function*(){
   yield this.proxy({
    data1:'github:repos/xiongwilee/data1',
    data2:'github:repos/xiongwilee/data2',
    data3:'github:repos/xiongwilee/data3'
   })
 }

koa会自动并发请求所有接口,然后将结果返回给你,然后可以在上下文的backData中获取结果集。

另外一种场景,比如用户需要上传一张图片或者提交一篇文章,你也可以这样写:

submit: function*(){
  yield this.proxy('http://test.com/submit')
}

不用做任何配置,koa会直接把request数据buffer直接通过管道pipe给后端接口。

值得一提的是:

  • 有没有发现,这种proxy方式是没有同域机制的限制 了,前端面试常问的跨域方案都不是事儿;

  • 另外,同学们可能觉得,本来一个请求都能搞定的事情非要用nodejs代理一次,会不会很慢?事实上,我们的测试结果显示:内网访问的情况下,nodejs代理耗时不超过10ms

最后,我们也做了一个压测:8核CPU/8G内存的机器,通过一个路由将请求代理到另外一个接口,结果显示:8核CPU/8G至少能扛住300QPS的压力,这个结果比纯路由的压测情况要低很多,值得注意。

不过有意思的一点是,我们压测发现:proxy的性能与接口响应时间无关,与接口响应content大小有密切关系:接口响应内容越大,proxy性能越差

3. 更灵活地环境部署

基于koa可以实现各类有意思的中间件。比如:可以拦截请求计算请求总耗时中间件、可以匹配路径实现本地的静态文件服务器、可以不需要再另开server实现一个mock数据的功能……

a) 开发环境

上文提到过一个DEMO的文件目录,具体在开发环境和生产环境中整体目录结构中其实是这样的:

├── app                // 开发模式下应用模块目录
│   ├── blog
│   └── demo
├── cli                // 配套命令行工具
│   ├── bin
│   ├── lib
│   └── package.json
├── log                // 日志目录
└── server            // 服务器目录
    ├── app            // 实际服务端引用的模块目录
    │   ├── blog
    │   └── demo
    ├── bin
    ├── config
    ├── src
    └── package.json

在开发环境下,你可以将./app下的项目源文件直接编译到./server/app目录,这就意味着:./app/目录下的各个项目,你自己想怎么玩就怎么玩

我们目前有基于gulp+requirejs的模块化方案和基于webpack+vue的前端构建方案,其中gulp+requirejs的方案已经开源:https://github.com/xiongwilee...

基于上文提到的路由和数据代理的功能,现在我们的开发模式就可以优化为:前后端确定接口→前后端同时开发→联调提测上线。给开发带来的好处自不必说:

  • 后端接口完全解耦,前端利用异步并发,性能可以达到最优;

  • WebAPP和NativeAPP可以通用一套接口,后端省了不少成本;

  • 前后端独立之后,前端构建的成本更低,灵活性更高;

  • ……

b) 生产环境

生产环境的部署与大多数服务部署类似:经过SLB之后到nginx进行反向代理,走到nodejs服务响应请求;可以在nginx层进行负载均衡和监控;如果需要用到缓存,可以在nginx和nodejs之间再加一层Varnish

这里必须要推荐下最牛哔的nodejs进程管理工具:pm2,完成了趣店生产环境下nodejs进程管理和nodejs日志切分。

然后提一下,我们的沙盒环境和测试环境部署。

沙盒只需要摘一台性能比较low的线上机器,提供给办公网络访问就行。因为数据代理的接口直接走的服务端对应环境的接口,整个沙盒环境和测试环境的搭建也非常愉快。

另外,我们对线上业务做了一个压测:单台8核CPU/8G机器,能扛住500QPS左右的压力

最后,关于环境部署,再提两个场景:

  • 一个场景是,超越前后端分离框架的本职工作,实现团队文档系统

因为前后端分离框架本身是不支持数据库和SESSION存储功能的,为了满足一些简单的数据库和文件上传需求,我们自己实现了两个中间件,一个能够利用mongoose很简单地与mongo打交道,一个利用formidable,koa-send模块轻易实现文件上传和下载;从而很快实现了团队博客和文档系统。

当然了,这个文档系统和团队博客仅限在内网系统中使用。

  • 另外一场景是,后端环境使用Java MVC框架下的前后端独立开发

我们团队与蚂蚁金服团队深度合作(事实上是作为TP,实现支付宝下的业务需求),必须使用蚂蚁金服的基于Java的sofalite MVC框架,而且坑爹的是:蚂蚁金服不能提供他们的前端构建框架!

这就意味着:我们团队的前端同学需要回到刀耕火种时代,跟后端同学用一个仓储自己搭建Java环境进行本地开发了。

但是,别忘了,我们现在有基于koa的前后端分离框架啊(知乎腔)!

事实上,前后端独立开发的唯一瓶颈就在与Java的velocity模板引擎;而我们的前后端分离框架的模板引擎是可配的。

所以,前后端定好了接口,然后前端就愉快地用上mock数据独立开发去了;最后,整个项目提前四天开发联调完成

四、koa-grace

主(guang)角(gao)终于要出场了!

这套前后端分离框架——koa-grace:基于koa的前后端分离框架已经开源。欢迎各路大神star/fork/提ISSUE,谢谢!!!

另外,配套命令行工具暂时仅限内网使用,后续会开源出来。最后,关于koa-garce还得啰嗦两点:

1)koa-grace是基于koa 1.x版本

目前koa-grace所有的中间件都是基于koa 1.x,而koa 2.x正式发布之后会立刻跟进。

但koa-grace毕竟只是一个轻量级的框架:Requiring babel to run your server is just not good developer experience.(引自:koa/issues/533)。

2)koa-grace需要完善测试用例

vue的作者尤雨溪说过,不写测试的开源项目不是合格的开源项目。而koa-grace目前还没有测试用例覆盖,希望大家一起来完善。

最后

无论前端怎么玩、无论技术怎么发展,任何架构最终都是为了服务于产品

减少技术成本,提升迭代效率和产品体验是Web技术的核心诉求(尤其是在做产品不快则死的创业公司)。

趣店产品项目在逐渐升级,我们也在随着产品升级而将业务迁移到这套前后端分离架构上来;目前来看,koa-grace给趣店产品和技术部署带来了不少便利。

就说到这儿,老板喊我去写PHP了…………

查看原文

赞 29 收藏 137 评论 7

lancer07 发布了文章 · 2016-06-02

用CSS3做各种样式的饼图

通过css3的旋转属性来实现饼图的效果。封装了一个饼图类,通过实例化来生成各种样式的饼图。

以前一直用highcharts做饼图的效果,但有时候,一些比较简单的饼图用highcharts的话有点杀鸡用牛刀的感觉。所以自己研究了一下如何用css3来达到这个效果。并封装了一下,支持多种样式,写了一个 pie.js

原理是这样的,父容器是一个div,背景颜色是左边一半灰色,右边一半绿色。

里面有2个可以旋转的div,小于50%的时候,第一个div是灰色的,顺时针旋转一下就好了,即用灰色遮绿色。

如果超过50%了,则需要用到第二个绿色的div,也是顺时针旋转一下,用绿色遮盖灰色的底。

有兴趣的同学可以fork一下下面的地址看一下源码。

github传送门:http://lancer07.github.io/css3_pie/

效果 如图所示
图片描述

使用也很方便,html是这样的,用data-value属性来存要显示的百分比。

<div class="pie" id="colorful-pie" data-value="0.2"></div>

然后实例化一个饼图类,可配可不配一些参数。

var myPie = new Pie({
    el : ‘#colorful-pie’,  // 选择器
    animite : true,        // 是否要有动画效果
    ring : 0.7,            // 中间是否要空心,并设置空心的半径 
    color : '#ff9933'      // 自定义饼图的颜色,
    number :false,         // 是否要显示中间的百分比
    rotate : -40           // 设置起点角度,默认是从12点钟方向开始的
});
myPie.init();    // 实例化

这样就生成了一个饼状图了。

PS:
应该还有更好的方法来实现,希望大家能告诉我。多谢!

查看原文

赞 5 收藏 27 评论 5

lancer07 发布了文章 · 2016-05-23

【二次元的CSS】—— CSS3画的能换频道的电视机(合集)

帮大家回忆一下,小时候的电视机。每到星期二的下午,总是这个图案。

GitHub传送门:https://github.com/lancer07/css3TV

这次分2步来做, 先画电视机,再画 《没有信号》的界面。好像也没什么难点,纯体力活。

这次绝笔之作中,把之前所有用css3画的东西都放进电视机里了。所以电视机是可以切换频道的。

图片描述

图片描述

图片描述

这次不贴代码了,有点多。有兴趣的同学就fork一下,看看源码好了。

收工

查看原文

赞 3 收藏 3 评论 0

lancer07 发布了文章 · 2016-05-20

【二次元的CSS】—— 纯CSS3做的能换挡的电扇

这次分享的电扇,和以往用css3画人物相比 多加了一点交互,就是电扇开关的地方,用到了一点点css3的 :checked +div 这个很少用到的选择器来实现的。

GitHub传送门:https://github.com/lancer07/css3_fan

效果是这样的
图片描述

HTML 结构

小技巧就是,一开始就写了一组单选按钮来做开关的部分。把单选按钮的透明度设为0,然后在后面叠加一个div来画开关的样式。

       <div id="fan">
            <input type="radio" name="switch" class="switch_0"/>
            <div class="switch_btn_0">0</div>
            <input type="radio" checked name="switch" class="switch_1"/>
            <div class="switch_btn_1">1</div>
            <input type="radio" name="switch" class="switch_2"/>
            <div class="switch_btn_2">2</div>
            <input type="radio" name="switch" class="switch_3"/>
            <div class="switch_btn_3">3</div>
            <div class="mask">
                <div class="logo">CSS3 Fan</div>
                <div class="line_1"></div>
                <div class="line_2"></div>
                <div class="line_3"></div>
                <div class="line_4"></div>
                <div class="line_5"></div>
                <div class="line_6"></div>
                <div class="leafs">
                    <div class="leaf_1"></div>
                    <div class="leaf_2"></div>
                    <div class="leaf_3"></div>
                </div>
            </div>
            <div class="header"></div>
            <div class="neck"></div>
            <div class="neck_footer"></div>
            <div class="bottom"></div>
            <div class="bottom_footer_1"></div>
            <div class="bottom_footer_2"></div>
        </div>

开关部分的LESS代码如下

   .switch{
        position: absolute;
        width: 24px;
        height: 24px;
        top: 296px;
        z-index: 13;
        opacity: 0;
        cursor: pointer;
    }

   .switch_0{
        .switch;
        left: 50px;
        &:checked+div{
            &+input+div+input+div+input+div+.mask{
                .leafs{
                    animation-play-state:paused;
                }
            }
        }
    }
    .switch_1{
        .switch;
        left: 75px;
        &:checked+div{
            background: #a9af27;
            color: #0e6873;
            &+input+div+input+div+.mask{
                .leafs{
                    animation-duration : 0.7s
                }
            }
        }
    }
    .switch_2{
        .switch;
        left: 100px;
        &:checked+div{
            background: #a9af27;
            color: #0e6873;
            &+input+div+.mask{
                .leafs{
                    animation-duration : 0.4s
                }
            }
        }
    }
    .switch_3{
        .switch;
        left: 125px;
        &:checked+div{
            background: #a9af27;
            color: #0e6873;
            &+.mask{
                .leafs{
                    animation-duration : 0.3s
                }
            }
        }
    }

看起来有点low。

至于电扇的CSS样式部分就不再赘述了。

有个地方需要优化,就是在换挡的时候,动画应该柔和些。有同学想到用什么办法优化下吗?

查看原文

赞 4 收藏 12 评论 0

lancer07 发布了文章 · 2016-05-17

【二次元的CSS】—— 用 DIV + CSS3 画大白(详解步骤)

原本自己也想画大白,正巧看到一位同学(github:https://github.com/shiyiwang)也用相同的方法画了。 且细节相当到位。所以我就fork了一下,在此我也分享一下。
同时,我也希望有更多的同学发挥自己的想象力,来找个东西画画。

图片描述

如果看过我前几次的分享,肯定能马上想到大白的各个部位是怎么实现的。

GitHub传送门:https://github.com/lancer07/css3_Baymax

第一步:头

图片描述

<div class="baymax-head">
  <div class="head-highlight"></div>
  <div class="baymax-eyes"></div>
</div>
.baymax-head {
  position: absolute;
  left: 50%;
  margin-left: -21px;
  width: 42px;
  height: 28px;
  -webkit-border-radius: 50%;
  -moz-border-radius: 50%;
  -ms-border-radius: 50%;
  border-radius: 50%;
  -webkit-transform: rotate(5deg);
  -moz-transform: rotate(5deg);
  -ms-transform: rotate(5deg);
  -o-transform: rotate(5deg);
  transform: rotate(5deg);
  z-index: 3;
  overflow: hidden;
  -webkit-box-shadow: 0 6px 8px -5px rgba(128, 128, 128, 0.75), inset 0 -6px 8px -5px rgba(204, 204, 204, 0.5);
  -moz-box-shadow: 0 6px 8px -5px rgba(128, 128, 128, 0.75), inset 0 -6px 8px -5px rgba(204, 204, 204, 0.5);
  box-shadow: 0 6px 8px -5px rgba(128, 128, 128, 0.75), inset 0 -6px 8px -5px rgba(204, 204, 204, 0.5);
}

.baymax-head .head-highlight {
  position: absolute;
  top: 12%;
  right: 25%;
  width: 45%;
  height: 1%;
  -webkit-border-radius: 50%;
  -moz-border-radius: 50%;
  -ms-border-radius: 50%;
  border-radius: 50%;
  background: #ffffff;
  -webkit-box-shadow: 0 0 18px 9px #ffffff, 0 0 0 0 transparent;
  -moz-box-shadow: 0 0 18px 9px #ffffff, 0 0 0 0 transparent;
  box-shadow: 0 0 18px 9px #ffffff, 0 0 0 0 transparent;
}

.baymax-head .baymax-eyes {
  position: relative;
  top: 10px;
  left: 50%;
  -webkit-transform: translateX(-10px);
  -moz-transform: translateX(-10px);
  -ms-transform: translateX(-10px);
  -o-transform: translateX(-10px);
  transform: translateX(-10px);
  height: 1px;
  width: 20px;
  background: #333333;
}

.baymax-head .baymax-eyes:before, .baymax-head .baymax-eyes:after {
  top: -3px;
  width: 6px;
  height: 6px;
  background: #333333;
  -webkit-border-radius: 50%;
  -moz-border-radius: 50%;
  -ms-border-radius: 50%;
  border-radius: 50%;
}

.baymax-head .baymax-eyes:before {
  left: -2px;
}

.baymax-head .baymax-eyes:after {
  right: -2px;
}

第二步:身体

图片描述

<div class="baymax-body">
  <div class="body-highlight"></div>
  <div class="baymax-heart"></div>
</div>
.baymax-body {
  position: absolute;
  top: 18.66667px;
  left: 50%;
  -webkit-transform: translateX(-62px);
  -moz-transform: translateX(-62px);
  -ms-transform: translateX(-62px);
  -o-transform: translateX(-62px);
  transform: translateX(-62px);
  width: 124px;
  height: 180px;
  -webkit-border-radius: 50% 50% 50% 50%/60% 60% 40% 40%;
  -moz-border-radius: 50% 50% 50% 50%/60% 60% 40% 40%;
  -ms-border-radius: 50% 50% 50% 50%/60% 60% 40% 40%;
  border-radius: 50% 50% 50% 50%/60% 60% 40% 40%;
  overflow: hidden;
  z-index: 2;
}

.baymax-body .body-highlight {
  position: absolute;
  top: 20%;
  right: 40%;
  width: 0%;
  height: 50%;
  -webkit-border-radius: 50%;
  -moz-border-radius: 50%;
  -ms-border-radius: 50%;
  border-radius: 50%;
  background: #ffffff;
  -webkit-box-shadow: 0 0 45px 25px #ffffff, 0 0 0 0 transparent;
  -moz-box-shadow: 0 0 45px 25px #ffffff, 0 0 0 0 transparent;
  box-shadow: 0 0 45px 25px #ffffff, 0 0 0 0 transparent;
}

.baymax-body .baymax-heart {
  position: absolute;
  top: 35px;
  right: 30%;
  height: 12px;
  width: 12px;
  -webkit-border-radius: 50%;
  -moz-border-radius: 50%;
  -ms-border-radius: 50%;
  border-radius: 50%;
}

.baymax-body:before, .baymax-body:after {
  top: 55px;
  width: 15px;
  height: 40px;
}

.baymax-body:before {
  left: -8px;
  -webkit-transform: rotate(15deg);
  -moz-transform: rotate(15deg);
  -ms-transform: rotate(15deg);
  -o-transform: rotate(15deg);
  transform: rotate(15deg);
}

.baymax-body:after {
  right: -8px;
  -webkit-transform: rotate(-15deg);
  -moz-transform: rotate(-15deg);
  -ms-transform: rotate(-15deg);
  -o-transform: rotate(-15deg);
  transform: rotate(-15deg);
}

第三步:双手

图片描述

<div class="baymax-arm baymax-left-arm">
   <div class="arm-highlight larm-highlight"></div>
</div>
<div class="baymax-arm baymax-right-arm">
   <div class="arm-highlight rarm-highlight"></div>
</div>
.baymax-arm {
  position: absolute;
  top: 28px;
  height: 145px;
  width: 40px;
  overflow: hidden;
  z-index: 1;
}

.baymax-arm:after {
  top: 60px;
  height: 50px;
  width: 25px;
}

.baymax-arm .arm-highlight {
  position: absolute;
  top: 25%;
  width: 1%;
  height: 65%;
  -webkit-border-radius: 50%;
  -moz-border-radius: 50%;
  -ms-border-radius: 50%;
  border-radius: 50%;
  background: #ffffff;
  -webkit-box-shadow: 0 0 16px 7px rgba(255, 255, 255, 0.8), 0 0 0 0 transparent;
  -moz-box-shadow: 0 0 16px 7px rgba(255, 255, 255, 0.8), 0 0 0 0 transparent;
  box-shadow: 0 0 16px 7px rgba(255, 255, 255, 0.8), 0 0 0 0 transparent;
}

.baymax-left-arm {
  left: 65px;
  -webkit-transform: rotate(25deg);
  -moz-transform: rotate(25deg);
  -ms-transform: rotate(25deg);
  -o-transform: rotate(25deg);
  transform: rotate(25deg);
  -webkit-border-radius: 90px 20px 20px 90px/200px 40px 40px 200px;
  -moz-border-radius: 90px 20px 20px 90px/200px 40px 40px 200px;
  -ms-border-radius: 90px 20px 20px 90px/200px 40px 40px 200px;
  border-radius: 90px 20px 20px 90px/200px 40px 40px 200px;
}

.baymax-left-arm:after {
  left: -20px;
}

.baymax-left-arm .larm-highlight {
  left: 30%;
}

.baymax-right-arm {
  right: 65px;
  -webkit-transform: rotate(-25deg);
  -moz-transform: rotate(-25deg);
  -ms-transform: rotate(-25deg);
  -o-transform: rotate(-25deg);
  transform: rotate(-25deg);
  -webkit-border-radius: 20px 90px 90px 20px/40px 200px 200px 40px;
  -moz-border-radius: 20px 90px 90px 20px/40px 200px 200px 40px;
  -ms-border-radius: 20px 90px 90px 20px/40px 200px 200px 40px;
  border-radius: 20px 90px 90px 20px/40px 200px 200px 40px;
}

.baymax-right-arm:after {
  right: -20px;
}

.baymax-right-arm .rarm-highlight {
  right: 30%;
}

第四步:双腿

图片描述

<div class="baymax-leg baymax-left-leg">
    <div class="leg-highlight lleg-highlight"></div>
    <div class="crosspart"></div>
</div>
<div class="baymax-leg baymax-right-leg">
    <div class="leg-highlight rleg-highlight"></div>
    <div class="crosspart"></div>
</div>
.baymax-leg {
  position: absolute;
  top: 165px;
  width: 48px;
  height: 85px;
  overflow: hidden;
  z-index: 1;
}

.baymax-leg:before {
  top: -50px;
  height: 100px;
  width: 30px;
}

.baymax-leg:after {
  bottom: -22px;
  height: 30px;
  width: 60px;
}

.baymax-leg .leg-highlight {
  position: absolute;
  top: 40%;
  width: 1%;
  height: 38%;
  -webkit-border-radius: 50%;
  -moz-border-radius: 50%;
  -ms-border-radius: 50%;
  border-radius: 50%;
  background: #ffffff;
  -webkit-box-shadow: 0 0 16px 7px rgba(255, 255, 255, 0.8), 0 0 0 0 transparent;
  -moz-box-shadow: 0 0 16px 7px rgba(255, 255, 255, 0.8), 0 0 0 0 transparent;
  box-shadow: 0 0 16px 7px rgba(255, 255, 255, 0.8), 0 0 0 0 transparent;
}

.baymax-leg .crosspart {
  content: "";
  position: absolute;
  top: 0;
  width: 48px;
  height: 85px;
  -webkit-box-shadow: inset 0px 0px 15px 0px #cccccc, 0 0 0 0 transparent;
  -moz-box-shadow: inset 0px 0px 15px 0px #cccccc, 0 0 0 0 transparent;
  box-shadow: inset 0px 0px 15px 0px #cccccc, 0 0 0 0 transparent;
}

.baymax-left-leg {
  left: 50%;
  margin-left: -50px;
  -webkit-border-radius: 20% 0 30% 50%/50% 0 30% 50%;
  -moz-border-radius: 20% 0 30% 50%/50% 0 30% 50%;
  -ms-border-radius: 20% 0 30% 50%/50% 0 30% 50%;
  border-radius: 20% 0 30% 50%/50% 0 30% 50%;
}

.baymax-left-leg:before {
  left: -20px;
}

.baymax-left-leg:after {
  left: 0;
}

.baymax-left-leg .lleg-highlight {
  left: 25px;
  -webkit-transform: rotate(-5deg);
  -moz-transform: rotate(-5deg);
  -ms-transform: rotate(-5deg);
  -o-transform: rotate(-5deg);
  transform: rotate(-5deg);
}

.baymax-right-leg {
  right: 50%;
  margin-right: -50px;
  -webkit-border-radius: 0 20% 50% 30%/0 50% 50% 30%;
  -moz-border-radius: 0 20% 50% 30%/0 50% 50% 30%;
  -ms-border-radius: 0 20% 50% 30%/0 50% 50% 30%;
  border-radius: 0 20% 50% 30%/0 50% 50% 30%;
}

.baymax-right-leg:before {
  right: -20px;
}

.baymax-right-leg:after {
  right: 0;
}

.baymax-right-leg .rleg-highlight {
  right: 20px;
  -webkit-transform: rotate(5deg);
  -moz-transform: rotate(5deg);
  -ms-transform: rotate(5deg);
  -o-transform: rotate(5deg);
  transform: rotate(5deg);
}

欢迎大家吐槽

查看原文

赞 3 收藏 17 评论 2

lancer07 发布了文章 · 2016-05-11

【二次元的CSS】—— 用 DIV + CSS3 画咸蛋超人(详解步骤)

仅仅使用div作为身体的布局,用css3的各种transform和圆角属性来绘制各部位的形状,当然也不会使用任何图片哦。那就没意思了。
有的同学说,用canvas不是能画得更逼真而且更简单吗?这点我也非常赞同,但我的理由还是,那就没意思了。

这次写的详细一点,把各个部位都拆出来分析。

GitHub传送门:https://github.com/lancer07/css3Ultraman

第一步:头部轮廓

图片描述

<header></header>
.ultraman header {
  border: 7px solid #000;
  border-top: 15px solid #000;
  width: 200px;
  height: 200px;
  border-radius: 50% 50% 60% 60%;
  position: absolute;
  background: #fff;
}

第二步:就算作是头发吧

图片描述

<header>
    <div class="hair"></div>        
</header>
.ultraman header .hair {
  position: absolute;
  top: -40px;
  left: 80px;
  width: 0;
  height: 0;
  border-left: 20px solid transparent;
  border-right: 20px solid transparent;
  border-top: 140px solid #000;
  border-radius: 30% 30% 50% 50%;
}

第三步:眼睛

图片描述

<header>
    <div class="hair"></div>
    <div class="left_eye"></div>
    <div class="right_eye"></div>
</header>

因为我是用less写的嘛,所以先定义了一个眼睛的类,然后再生成2个眼睛

.eye(@l,@r,@deg){
    border:5px solid #000;
    width:70px;
    height:70px;
    background:#ffc30a;
    border-radius:@l @r;
    transform:rotate(@deg);
    position:absolute;
    top:60px;
}
.left_eye{
    .eye(50%,80%,-15deg);
    left:10px;
}
.right_eye{
    .eye(80%,50%,15deg);
    right:10px;
}

第四步:耳朵

图片描述

<header>
    <div class="hair"></div>
    <div class="left_eye"></div>
    <div class="right_eye"></div>
    <div class="left_ear"></div>
    <div class="right_ear"></div>
</header>
.ear(@deg){
    width:20px;
    height:50px;
    border:5px solid #000;
    position:absolute;
    top:70px;
    z-index:-1;
    transform:rotate(@deg);
    background:#fff;
}
.left_ear{
    .ear(-7deg);
    left:-20px
}
.right_ear{
    .ear(7deg);
    right:-20px
}

第五步:小身体

图片描述

<div class="body">
    <div class="light"><span></span></div>
</div>

身上有个灯,时间到了,会嘀嘟嘀嘟叫的,所以加一个动画效果


@keyframes jump{
    0%{
        background:#48e1e7;
    }
    50%{
        background:#961e1e;
    }
    100%{
        background:#48e1e7;
    }
}

.body{
        width:100px;
        height:80px;
        background:#fff;
        border:7px solid #000;
        position:absolute;
        top:180px;
        left:50px;
        border-radius:0 0 20% 20%;
        z-index:-1;
        .light{
            width:40px;
            height:40px;
            border:3px solid #000;
            position:relative;
            top:20px;
            left:30px;
            background:red;
            transform:rotate(-45deg);
            span{
                width:8px;
                height:8px;
                border:2px solid #000;
                background:#48e1e7;
                display:block;
                position:absolute;
                left:3px;
                top:26px;
                border-radius:50%;
                z-index:2;
                animation:jump 0.5s infinite;
            }
        }
    }

第六步:手

图片描述

<div class="left_hand"></div>
<div class="right_hand"></div>

手只要旋转一下就好了,比出一个十字

.hand{
        width:30px;
        height:100px;
        border-radius:60% 60% 50% 50%;
        border:7px solid #000;
        position:absolute;
        background:#fff;
    }
    .left_hand{
        .hand;
        top:160px;
        left:30px;
    }
    .right_hand{
        .hand;
        top:160px;
        left:90px;
        transform:rotate(-90deg);
    }

第七步:裤子

图片描述

<div class="trousers"></div>
.trousers{
    border:7px solid #000;
    position:absolute;
    background:red;
    width:100px;
    height:45px;
    top:240px;
    left:50px;
    z-index:-2;
    border-radius:0 0 15% 15%;
}

第八步:腿

图片描述

<div class="left_footer"></div>
<div class="right_footer"></div>
<div class="egg"></div>

至于egg是什么, 我就不赘述了。

.footer{
        width:34px;
        height:80px;
        border-radius:50% 50% 60% 60%;
        border:7px solid #000;
        position:absolute;
        background:#fff;
        z-index:-3;
    }
    .left_footer{
        .footer;
        left:46px;
        top:260px;
        transform:rotate(20deg);
    }
    .right_footer{
        .footer;
        right:20px;
        top:270px;
        transform:rotate(-50deg);
    }
    .egg{
        background:#75d8f9;
        width: 18px;
        height: 30px;
        top: 286px;
        left: 97px;
        position: absolute;
        border-radius: 50%;
        border-top:7px solid #000;
    }

收工

欢迎大家吐槽

查看原文

赞 14 收藏 20 评论 3

lancer07 赞了文章 · 2016-05-10

typecho插件编写教程1 - 从HelloWorld说起

最近老高正在编写一个关于typecho的插件,由于typecho不像wordpress,有那么多的文档参考,写一个插件还是遇到了很多的坑,不过随着研究的不断深入,老高也慢慢上手了,于是总结出此篇编写教程分享给大家!

如果你对typecho的源码有兴趣,可以参考老高的系列文章

typecho源代码解析1 - 系统初始化
typecho源代码解析2 - 插件机制
typecho源代码解析3 - 路由分发
typecho源代码解析4 - 业务逻辑

从HelloWorld说起

基本信息

想必想要开发typecho的你一定阅读过官方示例插件HelloWorld的源码吧?

我们先看看usr/plugins/HelloWorld/Plugin.php文件前几行

phpif (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
 * Hello World
 * 
 * @package HelloWorld 
 * @author qining
 * @version 1.0.0
 * @link http://typecho.org
 */
 ...
 ...

这几行代码是一个插件的基本信息,我们由代码可以得出以下与插件相关的基本信息

插件说明 ---> Hello World
插件包名 ---> HelloWorld
插件作者 ---> qining
插件版本 ---> 1.0.0
插件链接 ---> http://typecho.org

同时这些信息都会显示在插件页中,如下图

插件基本信息

插件结构

我们继续向后面的代码看,一个最简单的插件结构如下(为了缩短篇幅,老高移除了具体方法的实现)

每个方法基本都有注释,老高不再赘述。

看起来很简单吧?其实里面还是有不少坑的。

phpclass HelloWorld_Plugin implements Typecho_Plugin_Interface
{
    /**
     * 激活插件方法,如果激活失败,直接抛出异常
     * 
     * @access public
     * @return void
     * @throws Typecho_Plugin_Exception
     */
    public static function activate(){}

    /**
     * 禁用插件方法,如果禁用失败,直接抛出异常
     * 
     * @static
     * @access public
     * @return void
     * @throws Typecho_Plugin_Exception
     */
    public static function deactivate(){}

    /**
     * 获取插件配置面板
     * 
     * @access public
     * @param Typecho_Widget_Helper_Form $form 配置面板
     * @return void
     */
    public static function config(Typecho_Widget_Helper_Form $form){}

    /**
     * 个人用户的配置面板
     * 
     * @access public
     * @param Typecho_Widget_Helper_Form $form
     * @return void
     */
    public static function personalConfig(Typecho_Widget_Helper_Form $form){}

    /**
     * 插件实现方法
     * 
     * @access public
     * @return void
     */
    public static function render(){}
}

插件流程

插件的基本流程是这样的。

  1. 当我们的插件写好后会出现在后台
  2. 点击启用按钮后,会执行对应插件类的activate方法
  3. 插件与目标插件点关联,等待触发
  4. 当点击停用的时候调用deactivate方法

本节完。

下一节老高会更详细的说明插件类的方法。

查看原文

赞 4 收藏 8 评论 0

lancer07 赞了文章 · 2016-05-10

typecho插件编写教程7 - Helper类

此文原本发表于我的博客 老高的技术博客 ,欢迎和老高交流!


Helper类为我们封装了很多与插件有关的操作,并且全部是公共静态方法,比如获取系统配置、添加路由、添加面板等功能,是开发插件必不可少的工具。

插件帮手将默认出现在所有的typecho发行版中.因此你可以放心使用它的功能, 以方便你的插件安装在用户的系统里.

Helper类的注释已经写的很清楚了,老高在此仅挑几个常用的方法讲讲。

获取配置

php# 获取系统配置
Helper::options();

# 获取指定插件配置
Helper::options()->plugin('BaiduSubmit');

# 获取当前插件配置
Helper::options()->plugin(str_replace('_Plugin','',__CLASS__));

路径安全

用于生成安全的URL,带有token验证,如果牵扯到action等核心操作时会用到。系统在验证请求是会用到$this->widget('Widget_Security')->protect()去验证来源token。

php$s = Helper::security();
echo $s->index('/');

# 输出类似 http://www.phpgao.com/?_=ce4a9617aa228bb85ba696c9ba9784be

强行删除某插件

此方法用于卸载插件失败时的替补方法,老高一般将此方法写入HELLO_WORLD插件的render方法里,这样刷新以下后台,出问题的插件就被卸载了。

Helper::removePlugin('BaiduSubmit');

路由增删

如果你的插件需要给系统添加一个新的路由,那么此方法就能派上用场!

路由表保存在表 typecho_options 中的 routingTable中。

比如老高的插件BaiduSubmit,就用此方法为系统添加了一个http://yourweb/baidu_sitemap.xml的路由。

/**
 * 增加路由
 *
 * @access public
 * @param string $name 路由名称
 * @param string $url 路由路径
 * @param string $widget 组件名称
 * @param string $action 组件动作
 * @param string $after 在某个路由后面
 * @return integer
 */
Helper::addRoute('baidu_sitemap', '/baidu_sitemap.xml', 'BaiduSubmit_Action', 'sitemap');

不要忘记在卸载的时候调用Helper::removeRoute('baidu_sitemap');删除刚才添加的路由!

Action

有心的同学应该能够发现,后台发送文章的form表单的action地址为类似http://web/action/contents-post-edit?_=xxx,这个链接表示什么意思呢?请听老高分解!

Action是typecho中的一类操作,它对应一条特殊的路由/action/,也就是路由表中的$table[0]['do'],具体内容如下

php [do] => Array
(
        [url] => /action/[action:alpha]
        [widget] => Widget_Do
        [action] => action
        [regx] => |^/action/([_0-9a-zA-Z-]+)[/]?$|
        [format] => /action/%s
        [params] => Array
            (
                [0] => action
            )

    )

从路由表能够看出,下一步我们需要找到类Widget_Do。我们能在该类中找到系统默认的action对应关系!

php    private $_map = array(
        'ajax'                      =>  'Widget_Ajax',
        'login'                     =>  'Widget_Login',
        'logout'                    =>  'Widget_Logout',
        'register'                  =>  'Widget_Register',
        'upgrade'                   =>  'Widget_Upgrade',
        'upload'                    =>  'Widget_Upload',
        'service'                   =>  'Widget_Service',
        'xmlrpc'                    =>  'Widget_XmlRpc',
        'comments-edit'             =>  'Widget_Comments_Edit',
        'contents-page-edit'        =>  'Widget_Contents_Page_Edit',
        'contents-post-edit'        =>  'Widget_Contents_Post_Edit',
        'contents-attachment-edit'  =>  'Widget_Contents_Attachment_Edit',
        'metas-category-edit'       =>  'Widget_Metas_Category_Edit',
        'metas-tag-edit'            =>  'Widget_Metas_Tag_Edit',
        'options-discussion'        =>  'Widget_Options_Discussion',
        'options-general'           =>  'Widget_Options_General',
        'options-permalink'         =>  'Widget_Options_Permalink',
        'options-reading'           =>  'Widget_Options_Reading',
        'plugins-edit'              =>  'Widget_Plugins_Edit',
        'themes-edit'               =>  'Widget_Themes_Edit',
        'users-edit'                =>  'Widget_Users_Edit',
        'users-profile'             =>  'Widget_Users_Profile'
    );

由此映射关系我们知道,当我们需要发布文章的时候(contents-post-edit),就去调用Widget_Contents_Post_Edit的action方法即可。

当我们要添加action对应关系的时候,就可以调用Helper:addAction()执行添加,系统就会添加一条'new_action' => 'some_action'

新增的动作保存在表 typecho_options 中的 actionTable中。

需要提醒的是,当我们添加一条action后,需要让其实现类some_action实现Widget_Interface_Do的接口,即必须有实现public function action(),否则调回失败。

同route一样,在卸载插件时不要忘了移除action!

菜单 & 面板

这个很好理解,不再赘述。

手动配置

当我们需要更自由的配置功能时,可以在Plugin.php文件中添加public static function configHandle($config)方法,当后台保存插件设置的时候就会调用此方法而不是默认的简单保存配置。

phphelper::configPlugin('BaiduSubmit', $config);

完!

查看原文

赞 2 收藏 4 评论 1

lancer07 发布了文章 · 2016-05-10

【二次元的CSS】—— 用 DIV + LESS 做一个小黄人构造器

仅仅使用div作为身体的布局,用css3的各种transform和圆角属性来绘制各个细节的形状,当然也不会使用任何图片哦。那就没意思了。

有的同学说,用canvas不是能画得更逼真而且更简单吗?这点我也非常赞同,但我的理由还是,那就没意思了。

这次用到了一些LESS的特性,通过设置一些指定的参数来生成不同种类、不同身材的小黄人。

GitHub传送门:https://github.com/lancer07/css3Minons

效果是这个样子的

图片描述

首先 先做个标准版的(ps:也就是图中的第一个小黄人)

HTML结构如下:(ps:每个小黄人的html结构都是一样的)

<div class="minions">
            <div class="hairs">
                <div class="hair1"></div>
                <div class="hair2"></div>
                <div class="hair3"></div>
            </div>
            <div class="body">
                <div class="cloth"></div>
                <div class="straps left-straps">
                    <div class="fastener"></div>
                </div>
                <div class="straps right-straps">
                    <div class="fastener"></div>
                </div>
            </div>
            <div class="glasses-type"></div>
            <div class="glasses left-glasses">
                <div class="eye">
                    <div class="ball">
                        <strong></strong></div>
                </div>
            </div>
            <div class="glasses right-glasses">
                <div class="eye">
                    <div class="ball">
                        <strong></strong></div>
                </div>
            </div>
            <div class="mouth">
                <div class="tooths">
                    <div class="line"></div>
                    <div class="tooth1"></div>
                    <div class="tooth2"></div>
                    <div class="tooth3"></div>
                </div>
            </div>
            <div class="arm left-arm">
                <div class="hand"></div>
            </div>
            <div class="arm right-arm">
                <div class="hand"></div>
            </div>
            <div class="pocket">
                <div>
                    <div></div>
                </div>
            </div>
            <div class="trousers"></div>
            <div class="leg left-leg">
                <div class="footer"></div>
            </div>
            <div class="leg right-leg">
                <div class="footer"></div>
            </div>
        </div>

LESS代码如下:(ps:先定义一个小黄人的类,然后通过设置参数来实例化每个小黄人)

定义小黄人

.Minion(@width:1;@height:1;@eye:2){
    width: 380px * @width;
    height:700px * @height;
    position:absolute;
    margin-top: -100px;
    margin-left:-20px;
    transform : scale(0.5,0.5);
    .hairs{
        position:absolute;
        top: -40px;
        z-index: 3;
        width: 100%;
        .hair{
            background:#000;
            width:2px;
            height:70px;
            position:absolute
        }
        .hair1{
            .hair;
            left:45%;
            transform:rotate(-20deg);
        }
        .hair2{
            .hair;
            left:50%;
        }
        .hair3{
            .hair;
            left:55%;
            transform:rotate(20deg);
        }
    }
    .body{
        overflow: hidden;
        background: #fff500;

        width: 380px * @width;
        position:absolute;
        z-index: 1;
        height:700px * @height;
        border-radius: 180px * @width;
    }
    .glasses-type{ //眼镜
        height:52px;
        background:#1f1a17;
        width:100%;
        position: absolute;
        top: 200px;
        z-index: 1;
    }
    
    .glasses{
        z-index: 2;
        position:absolute;
        background:#dededd;
        border:2px solid #1f1a17;
        width:150px;
        height:150px;
        border-radius: 50%;
        top: 140px;
        &.left-glasses when (@eye = 2){
            left:8%;
            .ball{
                //left : 45%;
                animation: eye 1.5s infinite ease;
            }
        }
        &.right-glasses when (@eye = 2){
            right:8%;
            .ball{
                //right:45%;
                animation: eye 1.5s infinite ease;
            }
        }
        &.left-glasses when (@eye = 1){
            left:50%;
            margin-left: -90px;
            width: 180px;
            height: 180px;
            .eye{
                width: 150px;
                height: 150px;
                .ball{
                    animation: eye 1.5s infinite ease;
                }
            }
        }
        &.right-glasses when (@eye = 1){
            display: none;
        }
        .eye{
            background:#fff;
            width:120px;
            height:120px;
            border-radius: 50%;
            border:2px solid #1f1a17;
            margin:15px auto;
            position:relative;
            .ball{
                background:#8f5444;
                width:40px;
                height:40px;
                border-radius: 50%;
                border:2px solid #1f1a17;
                position:absolute;
                top: 40%;
                transition: all .15s linear;
                strong{
                    display: block;
                    width:20px;
                    height:20px;
                    background:#1f1a17;
                    border-radius: 50%;
                    position:absolute;
                    top: 10px;
                    left:10px;
                }
            }
        }
    }

    .mouth{
        width:40%;
        height:80px;
        background:#fff;
        position:absolute;
        bottom:42%;
        left:30%;
        z-index: 1;
        border-radius: 120px 120px 40px 40px;
        border:2px solid #1f1a17;
        overflow:hidden;
        animation: up-down 0.5s infinite ease;
        .tooths{
            .tooth{
                border-right:2px solid #1f1a17;
                height:100%;
                width:0;
                position:absolute;
            }
            .tooth1{
                .tooth;
                left:25%;
            }
            .tooth2{
                .tooth;
                left:50%;
            }
            .tooth3{
                .tooth;
                left:75%;
            }
            .line{
                width:100%;
                top: 48%;
                border-top:3px solid #1f1a17;
                position:absolute;
            }
        }

    }
    .arm{
        position:absolute;
        width:50px;
        height:400px;
        background:#fff500;
        border-radius: 50px;
        top: 190px;
        z-index: 0;
        &.left-arm{
            left:-20px;
            transform:rotate(20deg);
        }
        &.right-arm{
            right:-20px;
            transform:rotate(-20deg);
        }
        .hand{
            position:absolute;
            bottom:0;
            width:60px;
            height:60px;
            border-radius: 50%;
            background:#1f1a17;
            left:-5px;
        }
    }
    .cloth{
        background:#667ab3;
        border-radius: 20px;
        bottom:20px;
        width:80%;
        height:250px;
        position:absolute;
        z-index: 1;
        left:10%;
    }
    .pocket{
        border:2px solid #1f1a17;
        border-radius: 5px 5px 30px 30px;
        width:100px;
        left:50%;
        margin-left: -50px;
        height:100px;
        position:absolute;
        z-index: 2;
        bottom: 80px;
        >div{
            background:#1f1a17;
            width:50px;
            height:50px;
            border-radius: 50%;
            top: 20px;
            left:25px;
            position:absolute;
            >div{
                width:20px;
                height:20px;
                border:5px solid #667ab3;
                transform:rotate(45deg);
                position:absolute;
                top: 10px;
                left:10px
            }
        }
    }
    .trousers{
        background:#667ab3;
        border-radius: 10px 10px 130px 130px;
        bottom:0;
        width:100%;
        height:160px;
        position:absolute;
        z-index: 1;
    }
    .straps{
        width:40px;
        height:150px;
        position:absolute;
        z-index: 1;
        background:#667ab3;
        bottom:230px;
        &.left-straps{
            left:10px;
            transform:rotate(-40deg);
        }
        &.right-straps{
            right:10px;
            transform:rotate(40deg);
        }
        .fastener{
            background:#1f1a17;
            width:20px;
            height:20px;
            border-radius: 50%;
            bottom:10px;
            position:absolute;
            left:10px;
        }
    }

    .leg{
        background:#667ab3;
        width:70px;
        height:120px;
        position:absolute;
        bottom:-80px;
        &.left-leg{
            left:20%;
            .footer{
                right:-2px;
                border-radius: 100px 0 0 20px;
            }
        }
        &.right-leg{
            right:20%;
            .footer{
                left:-2px;
                border-radius: 0 100px 20px 0;
            }
        }
        .footer{
            background:#1f1a17;
            width:100px;
            height:50px;
            position:absolute;
            bottom:0;
        }
    }
}

实例化

.minion-1{
    z-index: 1;
    top: 50px;
    left: 0;
    .Minion(1,1,2);    
}

.minion-2{
    z-index: 2;
    top: 0;
    left: 24%;
    .Minion(0.88,1.1,1);    
}

.minion-3{
    z-index: 2;
    top: 44px;
    left: 42%;
    .Minion(1.15,1.02,1);    
}

.minion-4{
    z-index: 1;
    top: 5px;
    left: 67%;
    .Minion(1,1.1,2);    
}

最后加点料

附加了2个小动画效果,眼睛转动和牙齿抖动。

@keyframes eye {
    0% {  
        transform:rotate(0,0);
    }
    50% {  
        transform:translate(70px,0px)
    }
    100% {  
        transform:translate(0px,0px)
    }
}

@keyframes up-down {
    0% {  
        transform:rotate(0,0);
    }
    50% {  
        transform:translate(0,2px)
    }
    100% {  
        transform:translate(0,0)
    }
}

后续

没有特别详细的描述每个细节部分,大家看一下源码或者fork一下就能知道具体每个元素是怎么实现的了。
当然这个肯定是有bug的,比如参数设置的过大或者过小,都会导致生成出来的小黄人乱七八糟,也欢迎大家吐槽。

查看原文

赞 6 收藏 11 评论 5

认证与成就

  • 获得 42 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-05-08
个人主页被 2.1k 人浏览