整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

CSS作图(二),纯CSS代码画出精致逼真的图像,学起来啊

SS的强大和精妙,只有在认真研读其代码之后才会深刻明白。

今日继续用纯CSS画图,并给大家整理了本文中所有代码,需要的小伙伴可以私信我哦。

一、阴阳

首先,用CSS画一个阴阳图,如下:

代码也很简单:

#yin-yang {
 width: 96px;
 height: 48px;
 background: #eee;
 border-color: red;
 border-style: solid;
 border-width: 2px 2px 50px 2px;
 border-radius: 100%;
 position: relative;
}
 
#yin-yang:before {
 content: "";
 position: absolute;
 top: 50%;
 left: 0;
 background: #eee;
 border: 18px solid red;
 border-radius: 100%;
 width: 12px;
 height: 12px;
}
 
#yin-yang:after {
 content: "";
 position: absolute;
 top: 50%;
 left: 50%;
 background: red;
 border: 18px solid #eee;
 border-radius:100%;
 width: 12px;
 height: 12px;
}

当然,颜色什么的,可以随便改啦~

二、鸡蛋

再画一个可爱的鸡蛋~~~像这样:

代码仅仅几句话:

#egg {
 display:block;
 width: 126px;
 height: 180px;
 background-color: red;
 -webkit-border-radius: 63px 63px 63px 63px / 108px 108px 72px 72px;
 border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
}

三、最后画一个弯弯的月亮~

长这样:

代码非常非常少:

#moon {

width: 80px;

height: 80px;

border-radius: 50%;

box-shadow: 15px 15px 0 0 red;

}

是不是很神奇?不敢相信这几句代码居然有这样的功力?

那就打开电脑,按照我之前讲的方法创建一个html试试吧~

不知道怎么创建的小伙伴看这里>>css3制作图形大全:简单几句代码画出漂亮的图形,一起来看看吧~


itHub 项目上好项目真的很多,但作者长的这么漂亮的肯定屈指可数了。

用 HTML + CSS 画油画

如图所示,这位小姐姐的名字叫 Diana Smith ,在她的个人博客上介绍自己是一位 JavaScript 开发人员,技能树包含 React / Vue / Backbone / jQuery ,还是一位 CSS 专家(LESS 和 SASS 双修),同时也是一位响应式设计的 UI 工程师。

她在 GitHub 上星数最高的一个项目,是用 HTML/CSS 创作了仿油画作品。

这吹弹可破的质感、精妙的构图、强烈又不违和的色彩搭配,不得不说“画如其人”啊。

而且,这些图片是只用 CSS 和 HTML,所有元素都是手动输入,仅允许使用 Atom 文本编辑器和 Chrome 开发者工具。

说实话,网页三兄弟 HTML、CSS、JavaScript 大家都不陌生,但谁能想到能玩出这样的花来呢?

作者小姐姐的个人博客中分享了一篇文章,其中有一个观点是:“当你只拥有一把 CSS 锤子时,世界就像是 CSS 钉子。”在工作之外,她也会用 CSS 来寻求艺术灵感。

作为前端,我们每天都在与这三兄弟打交道,但你真的了解他们么?你知道他们使用过程中的一些奇淫巧技么?

HTML、CSS、JavaScript 都是啥?

在各种技术论坛和贴吧里,前端算不算真正的程序员一直都是一个被讨论的问题。

但我觉得这个问题毫无意义啊,是又如何不是又如何,我只知道对于全世界所有的网站而言,无论其服务器端的语言是什么,前端都使用了 HTML + CSS。

很久之前我保存了一张微博上的图片,是一个前端工程师的技能树。虽然随着时间的推移很多内容都需要更新,但从图上我们也能看出前端工程师绝对不只是简单的切切页面而已。

前端工程师,首先是工程师,然后才是前端工程师。工程师的工作是用技术去解决业务上遇到的问题,所谓的前端后端只是分工不同。目前前端能容纳的知识领域越来越广,之后也将需要由更专业的人来完成工作,所以前端的路还可以走的很长。

今天,就和大家介绍一下网页三剑客分别是什么:

1. HTML

以前网页都是非常单纯的资讯呈现,所以有一些聪明的工程师想到,只要在电脑上装了一个浏览器,然后制定一个标准,让浏览器能正确呈现网页就好了。

这个标准就是 HTML,HTML 用 tag 的方式告诉浏览器资讯该如何呈现,举例:浏览器读到 image 这个 tag 就知道要在网页上摆一张图片,读到 button 就知道要在网页上放一颗按钮。

如此一来,我只要制定一系列常用的tag (例如<h1>,<h2>,<p>,<image>,<input>等等),就能让浏览器呈现图文并茂的网页。

一个好的网页其 HTML 可以说是相当单纯且具有易读性,这种网页不但方便前端工程师进行后续的维护外,也比较容易让你的网页增加曝光率让搜寻引擎能把你的网页摆在最前面,此种行为称为 SEO。

2. CSS

有了 HTML 后,大家还是不满意,因为单纯用 HTML 语言描述出来的网页很丑,工程师想出了一个方法,就是制定一种描述网页外观的语言,再让它变成一种标准,这样浏览器也就会知道如何美化网页了,这个语言就是 CSS。CSS 提供很多很厉害的效果和描述外观的属性,例如让图片变模糊、变亮、旋转,或者改变字体的颜色。

从 1997 年 CSS 1.0 发布到如今,从最开始只支持简单的文字排版到如今已经可以做出酷炫的 3D 动画,CSS 已经走过了 22 个年头,其发展如图所示:

随着互联网的发展,人们对网页的要求已经是从只要展示图文就好变成了各种交互跟视觉效果都需要有着更多的体验要求。CSS 为此也是不断的更新着。

3. JavaScript

但很多情况下仅凭 HTML + CSS 还是不够的。光是呈现画面总少了点趣味,我们希望网站能和使用者有互动,JavaScript 就是用来撰写这些互动行为的程式语言。

Javascript 能监听网页上的各种操作行为,例如移动滑鼠、点击按钮、网页缩放、输入文字等等。

总结一下:

  • HTML 建立搭建网页的主结构
  • CSS 负责网页的美化与优化
  • JavaScript 负责描述网页如何与使用者互动

但光学会这三样东西,仍然无法满足工程师开发上的需要,为了开发的效率,不想重新造轮子,所有的开发语言生态系都一定会衍生出各种 framework 和 library。

常见的 JavaScript 框架有 Vue.js、AngularJS、ReactJS、jQuery 等等,各有喜好者,但目的都是为了简化开发复杂度,把前后端分开,提高重复使用性,可维护性。

后话:前端真的又容易又没未来么?

很多人都说前端简单,但其实前端并不是简单,而是前端的学习反馈非常直接,上手容易并且很快就能见效,这也造成了前端很容易的假象。

但实际上,掌握 HTML + CSS + JavaScript,了解一些后端知识,只能说是一个合格的前端工程师;要想达到 80 分,就要了解业务,对于业务的需求和架构设计有真正的理解和设计;而行业内最稀缺的前端大牛,则超过了一般前端的范畴,更接近于「以前端开发为主的全栈工程师」。

在 20 年前,前端的概念其实还不常见,就连程序员这个大领域也没有过多的细分,基本上都是一个人或者几个人一起完成一个项目。

随着行业的变化和互联网的发展,情况和之前大不相同了。2010 年开始,企业需要更多在某一领域更专业、更垂直的开发者,也就造成了现在的前后端分离。

而现在情况又有所变化,用 Node.js、Meteor.js 这类运行环境来说,他们的出现让前端工程师也开始进行服务器开发,逐渐又在向全栈靠拢。

前不久有个观点很有意思,说是到了云时代,一家企业只要招几个前端工程师就可以了。

运维通过控制台就能完成,开发借助 Serverless 和编排工具就能实现无服务端。在更之后的未来,无论是前端工程师还是全栈工程师,都将不复存在,应该叫端到端(F2E -> E2E)工程师了。

所以我们不需要界定岗位的优劣,不管是前端还是后端,只要你有能力、有热情,都是稀缺的人才,学的一知半解想浑水摸鱼的人,也就只能是趁着行业的红利混口饭吃。

我们的开发工程中经常会使用到各种图,所谓的图就是由节点和节点之间的连接所形成的系统,数学上专门有一个分支叫图论(Graph Theroy)。利用图我们可以做很多工具,比如思维导图,流程图,状态机,组织架构图,等等。今天我要做的是用开源的HTML5工具来快速构造一个做图的工具。

工具选择

工预善其事,必先利其器。第一件事是选择一件合适的工具,开源时代,程序员还是很幸福的,选择很多。

  • flowchart.js http://adrai.github.io/flowchart.js/ , 基于SVG创建Flow Chart
  • go.js http://www.gojs.net/latest/index.html go.js 提供一整套的JS工具 ,支持各种交互式图表的创建。有免费版和收费版
  • joint.js http://www.jointjs.com/ joint.js 是另一个创建图标库的工具,也提供免费版和商业版
  • jsPlumb http://www.jsplumb.org/ jsPlumb是一套开源的流程图创建工具 ,小巧精悍,使用简单
  • d3 http://d3js.org 在html5领域,d3可谓是最好的可视化基础库,提供方面的DOM操作,非常强大。

最终,我选择了jsPlumb,因为它完全开源,使用很简单,用D3的话可能会多花很多功夫。joint.js也不错。大家可以根据自己的需要选择。

构建静态应用

下面我们一步一步的来使用jsPlumb来创建我们的流程图工具。

第一步是等待DOM和jsPlumb初始化完毕,类似document.ready()和jquery.ready(), 要使用jsPlumb, 需要把代码放在这个函数里:

jsPlumb.ready(function() {
    // ... your code goes here ...
}


创建一个jsPlumb的实例,并初始化jsPlumb的配置参数:

//Initialize JsPlumb
var color = "#E8C870";
var instance = jsPlumb.getInstance({
    // notice the 'curviness' argument to this Bezier curve.  the curves on this page are far smoother
    // than the curves on the first demo, which use the default curviness value.      
    Connector : [ "Bezier", { curviness:50 } ],
    DragOptions : { cursor: "pointer", zIndex:2000 },
    PaintStyle : { strokeStyle:color, lineWidth:2 },
    EndpointStyle : { radius:5, fillStyle:color },
    HoverPaintStyle : {strokeStyle:"#7073EB" },
    EndpointHoverStyle : {fillStyle:"#7073EB" },
    Container:"container-id"
 });


这里给给出了一些配置包括,连接线(这里配置了一个贝塞尔曲线),线的风格,连接点得风格。Container需要配置一个对应的DIV容器的id。(这里也可以使用setContainer的方法)

下面我们要创建一个节点(node),每一个节点可以用一个DIV来实现。我这里提供了一个函数来创建节点。

function addNode(parentId, nodeId, nodeLable, position) {
  var panel = d3.select("#" + parentId);
  panel.append('div').style('width','120px').style('height','50px')
    .style('position','absolute')
    .style('top',position.y).style('left',position.x)
    .style('border','2px #9DFFCA solid').attr('align','center')
    .attr('id',nodeId).classed('node',true)
    .text(nodeLable);

  return jsPlumb.getSelector('#' + nodeId)[0];
}


这里做的事情就是创建了一个DIV元素,并放在对应的容器的制定位置上,注意为了支持拖拽的功能,必须使用position:absolute 。

我使用D3来操作DOM,大家可能会更习惯JQuery,这纯属个人喜好的问题。

最后返回创建节点的实例引用,这是的selector使用了jsPlumb.getSelector()方法,它和JQuery的selector是一样的,这样用的好处是你可以使用不同的DOM操作库,例如Vanilla

下面我使用一个函数来创建端点/锚点(anchor),锚点就是节点上的连接点,用于连接不同的节点。

function addPorts(instance, node, ports, type) {
  //Assume horizental layout
  var number_of_ports = ports.length;
  var i = 0;
  var height = $(node).height();  //Note, jquery does not include border for height
  var y_offset = 1 / ( number_of_ports + 1);
  var y = 0;

  for ( ; i < number_of_ports; i++ ) {
    var anchor = [0,0,0,0];
    var paintStyle = { radius:5, fillStyle:'#FF8891' };
    var isSource = false, isTarget = false;
    if ( type === 'output' ) {
      anchor[0] = 1;
      paintStyle.fillStyle = '#D4FFD6';
      isSource = true;
    } else {
      isTarget =true;
    }

    anchor[1] = y + y_offset;
    y = anchor[1];

    instance.addEndpoint(node, {
      uuid:node.getAttribute("id") + "-" + ports[i],
      paintStyle: paintStyle,
      anchor:anchor,
      maxConnections:-1,
      isSource:isSource,
      isTarget:isTarget
    });
  }
}


instance是jsPlumb的实例

node是我们用addNode方法创建的Node实例

ports,是一个string的数组,指定端点的个数和名字

type,可能是output或者input,指定端点的种类,一个节点的输出端口可以连接另一个节点的输入端口。

这里anchor是一个四维数组,0维和1维分别是锚点在节点x轴和y轴的偏移百分比。我这里希望把端口画在节点的左右两侧,并按照端口的数量均匀分布。

最后使用instance.addEndpoint来创建端点。注意这里只要指定isSource和isTarget就可以用drag&drop的方式来连接端点,非常方便。

下面一步我们提供一个函数来连接端点:

function connectPorts(instance, node1, port1, node2 , port2) {
  // declare some common values:
  var color = "gray";
  var arrowCommon = { foldback:0.8, fillStyle:color, width:5 },
  // use three-arg spec to create two different arrows with the common values:
  overlays = [
    [ "Arrow", { location:0.8 }, arrowCommon ],
    [ "Arrow", { location:0.2, direction:-1 }, arrowCommon ]
  ];

  var uuid_source = node1.getAttribute("id") + "-" + port1;
  var uuid_target = node2.getAttribute("id") + "-" + port2;

  instance.connect({uuids:[uuid_source, uuid_target]});
}


node1和node2是源节点和目标节点的引用,port1和port2是源端口和目标端口的名字。

使用instance.connect方法来创建连接。 overlays用来添加连接线的箭头效果或者其他风格,我这里没有使用,因为觉得都不是很好看。大家如果要用,只要把overlays加入到instance.connect的方法参数就可以了。

调用以上方法来创建节点,端点和连接线。

var node1 = addNode('container-id','node1', 'node1', {x:'80px',y:'20px'});
var node2 = addNode('container-id','node2', 'node2', {x:'280px',y:'20px'});

addPorts(instance, node1, ['out1','out2'],'output');
addPorts(instance, node2, ['in','in1','in2'],'input');

connectPorts(instance, node1, 'out2', node2, 'in');


这里我们创建了两个节点,第一个节点有两个输出端口,第二个节点有三个输入端口,然后把第一个节点的out2端口连接到第二个端点的in端口。效果如下:

最后我们给节点增加drag&drop的功能,这样我们就可以拖动这些节点来改变图的布局了。

instance.draggable($('.node'));


这里似乎依赖于JQuery-UI,我还不是很清楚。

交互式创建节点

我们已经初步具有了创建图的功能,可是节点的创建必须通过程序,我们希望用交互的方式来创建节点。

通常我们希望有一个tree view的控件,让后通过拖拽来创建对应类型的节点。这里我使用了这个开源的tree view,基于bootstrap https://github.com/jonmiles/bootstrap-treeview

我们先创建一个tree view:

function getTreeData() {
  var tree = [
    {
      text: "Nodes",
      nodes: [
        {
          text: "Node1",
        },
        {
          text: "Node2"
        }
      ]
    }
  ]; 

  return tree;
}
//Initialize Control Tree View
$('#control-panel').treeview({data: getTreeData()});


树上有两个节点:

然后我实现从树上拖拽对应的节点,到流程图上的逻辑。

//Handle drag and drop
$('.list-group-item').attr('draggable','true').on('dragstart', function(ev){
  //ev.dataTransfer.setData("text", ev.target.id);
  ev.originalEvent.dataTransfer.setData('text',ev.target.textContent);
  console.log('drag start');
});

$('#container-id').on('drop', function(ev){
  //avoid event conlict for jsPlumb
  if (ev.target.className.indexOf('_jsPlumb') >= 0 ) {
    return;
  }

  ev.preventDefault();
  var mx = '' + ev.originalEvent.offsetX + 'px';
  var my = '' + ev.originalEvent.offsetY + 'px';

  console.log('on drop : ' + ev.originalEvent.dataTransfer.getData('text'));
  var uid = new Date().getTime();
  var node = addNode('flow-panel','node' + uid, 'node', {x:mx,y:my});
  addPorts(instance, node, ['out'],'output');
  addPorts(instance, node, ['in1','in2'],'input');
  instance.draggable($(node));
}).on('dragover', function(ev){
  ev.preventDefault();
  console.log('on drag over');
});


这里要注意的是要避免和jsPlumb拖拽端点的逻辑冲突,当检测到target是jsPlumb对象是需要直接从drop方法中退出以执行对应的jsPlumb的drop逻辑。

好了,一个绘制流程图的软件工具初步完工。

我把代码放在oschina的代码托管服务上了, 大家有兴趣可以去试试。