首页

酷炫的 FUI 图形设计

前端达人

作者通过以进度条的设计深入浅出的讲解关于FUI的设计思路。虽然FUI设计看起来很复杂,只要找准规律,从一些作品中总结分析细节,再运用到我们的设计中。想要了解的小伙伴可以尝试一下。

前言

看到上期小伙伴想了解一下 FUI 的设计教程,所以这次再次和大家聊一下关于 FUI 的设计风格,我们以“进度条”的设计教程为例,深入浅出的聊一下关于 FUI 的设计思路,因为主要设计思路的延展,所以不会出现具体的数据参数,小伙伴们要谅解啊。

目录

1.风格介绍

2.设计思路

3.总结

1.风格介绍

关于 FUI 的定义,阅读过我上一篇文章的小伙伴可能对 FUI 的定义有了一个较为清晰的定义,即 FUI 是:虚构的、未来的、幻想的用户界面。

1.png



我们仔细阅读关键词:“虚构的、未来的、幻想的。”从对 FUI 关键词上我们进行粗略的分析可以得出这么一条简单的结论:FUI 的设计不依据现实为基础的界面设计,为设计师留有巨大的想象空间。所以朋友们,请把脑洞开大一点吧,不受到当前的技术和硬件载体的制约,FUI 本身就是一种创新发明。

2.png




接下来我会向大家简单介绍一下 FUI 主要的两大设计风格:军事风格和机甲风格(也可以叫做机械风格)。 


1.军事风格

军事风格的特点在于:“直接明了”。因为在残酷的战争中时间就是生命,士兵必须保证“快速、准确、直接”才能保证战斗的胜利,所以会尽量避免与操作业务无关的。你也可以理解为当下最前沿的的设计理念“less is more。”例如下图,在界面设计中几乎都是利用简单的几何设计语言完成。

3.png




军事风格成为 FUI 的主流设计形式有着必然的原因。首先简单的从我们熟知的影视作品中来讲,在诸多科幻动作为主的故事题材的影视作品中,正义的一方都有军队的支持,其中不可避免的会出现的许多设备的界面;再则随着科技的发展,许多高科技都会用运用到军事设备的研发上,精密的结构线和反复数据让我们觉得其中的科技含量很高。


4.png




2.机甲风格

机甲风格相对于军事风格的最大特点就是:“具有一定的装饰图形元素,或多或少。”机甲风格最大的特点便是具有机械或者机甲风格的装饰元素,其中多为异形元素。其灵感来源于工业设计,从机械和机甲的工业设计语言中提取图形元素,在运用到界面设计上。如下图,异形的机甲风格让机器人的头部设计显得更加统一,如果换成当前流行的扁平化设计,可能就显得有点奇怪了。

5.png

机甲风格的发展归功于科幻题材故事的发展,为人们打开脑洞,畅想更多的可能性。在设计上激发了 FUI 的诞生,无论是赛博朋克、废土题材、还是漫威、DC宇宙中的科幻影视作品中我们都能看到机甲风格的界面设计。

6.png



2.设计思路

设计样式 – 军事风格

我们从以上的设计风格中抓去我们需要的关键词进行示例设计,首先我们看军事风格的关键词是“直接明了、快速、准确、直接,”我们转换成我们平时的设计语言就是:“极简风格,”这样是不是更好理解了。例如图例中,页面整体十分统计,利用简洁几何语言进行设计。

7.png




接下来我们做一个简单的军事风格的进度条:第一步,找参考!!!这一步很重要,很多同学都很容易就忽视这一点,一心一意的闭门造车,绝不借鉴学习其它优秀作品。这里向大家推荐 HUDS + GUIS 设计公司,这里有我们许多我们耳熟能详的影视作品中的 FUI 设计。

第二步,临摹,临摹可以说是学习他人技巧的最快方式,从中我们可以学习到许多设计中的细节,日后我们可以运用到自己的设计当中。我借鉴临摹了下面的进度条样式。


8.png



第三部,修改细节,举一反三。临摹就一定是是抄袭么?当然不是!创意设计,是把再简单不过的东西或想法不断延伸给予的另一种表现方式(百度百科),所以我们可以通过对设计组件内部进行重新组合或者修改其中的细节参数来达到自己在设计的目的。通过总结分析,我选用了最简单直白的结合图形作为设计元素进行设计,如下图:

9.png


我们可以放入界面当中感受一下视觉效果:

10.png



设计样式 – 机甲风格

我们再来进阶一下,设计一个机甲元素的的进度条。

机甲风格看似装饰图形复杂,设计难度复杂,但其实我们只需要掌握好 – “提炼元素”这项技能就能完美应对机甲风格的设计。

我们再回顾对创意设计的定义:是把再简单不过的东西或想法不断延伸给予的另一种表现方式。这里我们可以理解为将机甲元素进行提炼总结,延伸到弹框设计当中。例如下图,漂亮的小姐姐一秒钟变机械美女,就是通过对机械元素延展到模特身上。

11.png12.png13.png

第一步,照一张你喜欢的不错的参考,这里你可以找成熟的界面设计作品,也可以找一张不错的关于机甲风格的参考,以便于自己进行元素提取。这里我们以大家熟悉的高达为例:

14.png



第二部就是元素组合,我们需要提取了我们可能需要的元素,就像我们设计时面对自己手机素材一样,这个时候我们不要急于立马去设计,要仔细思考其设计形式,元素的位置安排。我们还是以以高达为例,途中我圈出了我可能用到的图形元素。


15.png



我们从中提取我们需要的图形,如下图:


16.png

最后我们我们需要仔细思考将图形进行组合,多尝试几次他们的组合方式。这里我对提取的元素进行了二次加工,将图形一和图形四进行了结合,打破固有的组合规律,让图形看起来更加生动。

17.png

最后我们可以放入界面当中感受一下视觉效果:

18.png



3.总结

FUI 的设计看起来复杂,难以下手,但其实也是有规律可循,我们可以从作品中理性的去分析其中的设计细节,提炼总结,最终再落实到实际的设计作品当中。当然最好你能先了解一下它的设计理念以及发展,就像 FUI 是“虚构的、未来的、幻想的界面设计”一样。

---来自姜正


转载自简书

作者:极创设计

链接:https://www.jianshu.com/p/77665c771153

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。



蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服


九宫格方式上传图片(预览并删除)

前端达人

<view class="gallery">

    <view class="item" wx:for="{{images}}" wx:key="">

        <image src="{{item}}" data-src="{{item}}" bindtap="previewImage" mode="aspectFill" />



        <!-- 删除按钮 -->

        <view class="delete" bindtap="delete" data-index="{{index}}">X</view>

    </view>

    <view class="item" bindtap="chooseImage">

        <view class='addIcon'>+</view>

    </view>

</view>

<button type="primary" bindtap="submit">提交</button>

————————————————



/* pages/index/index.wxss */
/*画廊*/
.gallery {    
  width:630rpx;
  margin: 0 auto;
  display: flex;    
  justify-content: flex-start;    
  flex-wrap: wrap;
}
/*每张图片所占容器*/
.item {    
  position: relative;    
  margin:10rpx 5rpx;
  width: 200rpx;
  height: 200rpx;
}
.item image{
  width: 100%;
  height: 100%;
}
/*add按钮*/
.item .addIcon{
  position:relative;
  width:200rpx;
  height:200rpx;
  text-align:center;
  line-height:200rpx;
  font-size:80rpx;
  background: #f2f2f2;
  color: #555;
}
/*删除按钮*/
.delete {    
  position:absolute;
  right:0;
  top:0;
  /* background:#ccc; */
  opacity:1;
  height: 36rpx;
  font-size:22rpx;
  font-weight:700;
  padding:0 8rpx 0 10rpx;
}
————————————————


var that;
Page({
  data: {
    images: [],
    uploadedImages: [],
    //imageWidth: getApp().screenWidth / 4 - 10
  },
  onLoad: function (options) {
    that = this; var objectId = options.objectId; console.log(objectId);
  },
  chooseImage: function () {
    // 选择图片
    wx.chooseImage({
      count: 3, // 默认9
      sizeType: ['compressed'],
      sourceType: ['album', 'camera'],
      // 可以指定来源是相册还是相机,默认二者都有
      success: function (res) {
        // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
        var tempFilePaths = res.tempFilePaths;
        console.log(tempFilePaths);
        that.setData({
          images: that.data.images.concat(tempFilePaths)
        });
      }
    })
  },
  // 图片预览
  previewImage: function (e) {
    //console.log(this.data.images);
    var current = e.target.dataset.src
    wx.previewImage({
      current: current,
      urls: this.data.images
    })
  },
  // submit: function () {        
  //   // 提交图片,事先遍历图集数组
  //   that.data.images.forEach(function (tempFilePath) {
  //     new AV.File('file-name', {
  //       blob: {
  //         uri: tempFilePath,
  //       },
  //     }).save().then(                
  //       // file => console.log(file.url())
  //     function (file) {                    
  //       // 先读取
  //       var uploadedImages = that.data.uploadedImages;
  //       uploadedImages.push(file.url());                    
  //       // 再写入
  //       that.setData({
  //         uploadedImages: uploadedImages
  //       }); console.log(uploadedImages);
  //     }
  //     ).catch(console.error);
  //   });
  //   wx.showToast({
  //     title: '评价成功', success: function () {
  //       wx.navigateBack();
  //     }
  //   });
  // }, 
  delete: function (e) {
    var index = e.currentTarget.dataset.index; var images = that.data.images;
    images.splice(index, 1);
    that.setData({
      images: images
    });
  }
})
————————————————


微信小程序转发/分享功能 小卡片设置

前端达人

<button data-name="shareBtn" open-type="share" plain="true">转发</button>

添加plain=”true”后button的边框样式可自定义 ↓ ↓


button[plain]{ border:0


 //转发
  onShareAppMessage: function (options) {
      var that = this;
      // 设置菜单中的转发按钮触发转发事件时的转发内容
      var shareObj = {
          title: "这是一个标题!",        // 默认是小程序的名称(可以写slogan等)
          //path: '/page/index/index/user?id=123',        // 默认是当前页面,必须是以‘/’开头的完整路径
          imageUrl: '../../img/xiaochengxu-share.jpg',     //自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径,支持PNG及JPG,不传入 imageUrl 则使用默认截图。显示图片长宽比是 5:4
          success: function (res) {
              // 转发成功之后的回调
              if (res.errMsg == 'shareAppMessage:ok') {
              }
          },
          fail: function (res) {
              // 转发失败之后的回调
              if (res.errMsg == 'shareAppMessage:fail cancel') {
                  // 用户取消转发
                       console.log("用户取消转发");
              } else if (res.errMsg == 'shareAppMessage:fail') {
                  // 转发失败,其中 detail message 为详细失败信息
              }
          },
            complete: function(){
              // 转发结束之后的回调(转发成不成功都会执行)
          },
    };
    // 来自页面内的按钮的转发
    if(options.from == 'button') {
        var eData = options.target.dataset;
        console.log(eData.name);     // shareBtn
        // 此处可以修改 shareObj 中的内容
        //shareObj.path = '/pages/btnname/btnname?btn_name=' + eData.name;
    }
// 返回shareObj
return shareObj;
————————————————



微信小程序轮播功能

前端达人

<swiper indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" interval="{{interval}}" duration="{{duration}}" circular="{{duration}}" current="{{swiperCurrent}}" bindchange="swiperChange" class="swiper">

  <block wx:for="{{imgUrls}}" wx:key="unique">

    <swiper-item>

      <image src="{{item}}" class="img" bindtap="swipclick" />

    </swiper-item>

  </block>

</swiper>

————————————————


/* swiper {
    height: 421.5rpx;
} */
swiper-item image {
    width: 100%;
    height: 100%;
}
.swiper-container{
  width: 100%;
  position: relative;
}
.swiper-container .swiper{
  height: 300rpx;
}
.swiper-container .swiper .img{
  width: 100%;
  height: 100%;
}
————————————————


const app = getApp()
Page({
  data: {
    swiperCurrent: 0,
    indicatorDots: true,
    autoplay: true,
    interval: 3000,//自动切换时间间隔
    duration: 800,//滑动动画时长
    circular: true,//是否采用衔接滑动
    imgUrls: [
      '../../img/index/1.jpeg',
      '../../img/index/2.jpeg',
      '../../img/index/3.jpeg'
    ]
  },
  //轮播图的切换事件
  swiperChange: function (e) {
    this.setData({
      swiperCurrent: e.detail.current
    })
    //console.log(e.detail.current);
  },
  //点击指示点切换
  chuangEvent: function (e) {
    this.setData({
      swiperCurrent: e.currentTarget.id
    })
  },
  //点击图片触发事件
  swipclick: function (e) {
    console.log(this.data.swiperCurrent);
    wx.switchTab({
      url: this.data.links[this.data.swiperCurrent]
    })
  },
})
————————————————



真机预览本地页面方式-简单快捷(精简大法Node)

前端达人

前提:真机和PC端在同一个局域网内。

1、安装nodejs环境 (node -v 查看版本号)



2、在所在的项目下输入命令:npm install anywhere -g



3、直接输入命令:anywhere,这里浏览器自动打开所有项目的根目录,点击就可以看到,同一网段下,然后手机直接预览这个地址就可以

————————————————


小程序单张图片 和 九宫格图片上传、预览、删除示例

前端达人

<view class="gallery">

  <view class='tipTitle'>

    快去上传自己的照片吧

  </view>

  <view class='item-ot'>

    <view class="item">

      <!-- 添加按钮 -->

      <view class="addIcon" bindtap="chooseImage" wx:if="{{imgBoolean}}">

          <view class=''>+</view>

      </view>

      <!-- 上传的图 -->

      <view class='itemImg' >

        <image src="{{item}}" data-src="{{item}}" bindtap="previewImage"  mode="aspectFill" />

        <!-- 删除按钮 -->

        <view class="delete" bindtap="deleteImg" data-index="{{index}}">X</view>

      </view>

      <view class='boxStyle'></view>

    </view>

    <view class='itemTxt'>正面照</view>

  </view>

  <view class='uploadFinish'>

    <a class="uploadFinishBtn" href="javasctipt:;"  bindtap="submit">提  交</a>

  </view> 

</view>


/*画廊*/
.gallery {    
  width:100%;
  margin: 0 auto;
  display: flex;    
  justify-content: flex-start;    
  flex-wrap: wrap;
  background: #fffaf0;
}
/*每张图片所占容器*/
.item-ot{
  margin:0 auto;
  width: 100%;
  height: 100%;
}
.item {    
  position:relative;
  margin:0 auto;
  width:370rpx;
  height:490rpx;
  background:#eee;
  border:2rpx solid #f9c4c2;
  /* overflow:hidden; */
}
.itemImg{
  position: absolute;
  left: 0;
  top:0; 
  width: 100%;
  height: 100%;
  overflow: hidden;
  z-index:1;
}
.itemImg image{
  width: 100%;
  height: 100%;
}
/*add按钮*/
 .addIcon{
   position:absolute;
  left: 0;
  top: 0; 
  width: 100%;
  height: 100%;
  text-align:center;
  line-height:490rpx;
  font-size:80rpx;
  background: #fff;
  color: #999;
  z-index:2;
}
/*删除按钮*/
.delete {    
  position:absolute;
  right:0;
  top:0;
  /* background:#ccc; */
  opacity:1;
  height: 36rpx;
  font-size:22rpx;
  font-weight:700;
  padding:0 8rpx 0 10rpx;
  color: #999;
}
.itemTxt{
  text-align: center;
  font-size: 30rpx;
  color: #999;
  margin-top: 50rpx;
  margin-bottom:  70rpx;
  font-weight: 700;
}
.uploadFinish{
  width: 100%;
  height: 100%;
  padding: 0 30rpx;
  box-sizing: border-box;
}
.uploadFinishBtn{
  background: #ff6666;
  color: #fff;
  display: block;
  width: 100%;
  padding: 26rpx 0;
  text-align: center;
  font-size: 36rpx;
  border-radius: 10rpx;
  margin-bottom: 40rpx;
}
.tipTitle{
  text-align: center;
  font-size: 30rpx;
  color: #f6a29d;
  font-weight: 700;
  width: 100%;
  margin: 50rpx 0;
}
.boxStyle{
  width:300rpx;
  height:100rpx;
  position:absolute;
  bottom:-1rpx;
  border-radius:50%;
  box-shadow:0rpx 10rpx 100rpx #fddbd9;
  margin-left:35rpx;
}


Page({
  data: {
    uploadedImages: [],
    imgBoolean: true,
  },
  onLoad: function (options) {
    var that = this;
  },
  chooseImage: function () {
    var that = this;
    // 选择图片
    wx.chooseImage({
      count: 1, // 默认9
      sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
      sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
      success: function (res) {
        // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
        var tempFilePaths = res.tempFilePaths
        that.setData({
          item: tempFilePaths[0],
          imgBoolean: false
        });
      }
    })
  },
  // 图片预览
  previewImage: function (e) {
    var current = e.target.dataset.src
    wx.previewImage({
      current: current,
      urls: [current]
    })
    console.log("这是1" + current);
  },
  //删除图片
  deleteImg: function (e) {
    var that = this;
    var images = that.data.uploadedImages;
    that.setData({
      uploadedImages: images,
      imgBoolean: true
    });
  },
  // submit: function () {        

  // }, 
})


charts柱状图的X轴Y轴加单位的写法

前端达人

  •         {
  •             type : 'value',
  •             axisLabel:{formatter:'{value} %'}
  •         }
  •     ],

  • 如果想控制百分比最大到100% 可添加


  • yAxis : [
     
            {
                type : 'value',
                max:100,//Y轴最大值 不写的话自动调节
                axisLabel:{formatter:'{value} %'}
            }
     
        ],
    > max:100,//Y轴最大值 不写的话自动

  • table点击实现可编辑文本

    前端达人

    table点击实现可编辑文

    一个简单的例子,直接添加contentEditable=”true”标签属性即可

    <table>
        <tr>
            <td>姓名:</td>
            <td contentEditable="true"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td contentEditable="true"></td>
        </tr>
    </table>

    VeeValidator

    前端达人

    VeeValidator

    语言设置

    校验消息默认是英文的,定义中文或其他语言的错误提示消息

    
    
    1. import VeeValidate from 'vee-validate';
    2. import Vue from 'vue'
    3. Vue.use(VeeValidate)
    4. var dict = {
    5. zh_CN: {
    6. messages: {
    7. required: function(field){
    8. return field + '不能为空!';
    9. },
    10. between: function(field){
    11. return field + '输入不符合设定规则!';
    12. },
    13. min : function (field,leng) {
    14. return field + '长度不能小于'+leng+'位';
    15. }
    16. }
    17. }
    18. };
    19. VeeValidate.Validator.localize('zh_CN', dict.zh_CN);

    校验的时候需要设置语言

    this.$validator.localize('zh_CN');

    错误消息显示

    显示指定字段的第一个错误

    this.$validator.first('fieldname')

    显示所有字段的第一个错误消息

    this.$validator.errors.all()

    Mock.js

    配置

    路由拦截配置不需要修改之前的代码,匹配的url请求会直接通过mock而不是请求服务器

    
    
    1. const handler = req => {
    2. return {mock数据};
    3. }
    4. Mock.mock('url拦截规则,正则表达式',handler)

    配置延迟时间

    模拟服务器请求的异步特性

    
    
    1. Mock.setup({
    2. timeout:1000
    3. })

    Vuex

    模块化

    多人协作,或者中大型的项目需要把store分为模块

    
    
    1. const a = {
    2. state : {foo:1},
    3. mutations : {hello(state)=> {}},
    4. modules : {
    5. ...嵌套
    6. }
    7. }
    8. const b = {}
    9. const store = {
    10. state : {},
    11. mutations : {},
    12. actions : {},
    13. modules : {
    14. module_name_a:a,
    15. module_name_b:b
    16. }
    17. }

    在调用的时候,state 有命名空间的,而mutation和actions都与父模块共用同样的命名空间所以不能定义与父模块同名的mutation 或 action

    获取模块的state

     this.$store.state.module_name_a.foo

    调用模块的mutation

    this.$store.commit('hello')

    namespace

    定义了namespace ,mutations 和 action 会带上模块的命名: module_name/muation

    
    
    1. const store = {
    2. modules : {
    3. namespace : true,
    4. a: {
    5. muations : {
    6. test(state) => {...}
    7. }
    8. }
    9. }
    10. }

    这时候调模块内的mutation

    this.$store.commit('a/test')

    vuejs-datepicker

    日期选择控件

    设置默认值

     <datepicker v-model="mydate" </datepicker> 

    日期格式化

    <datepicker :format="'yyyy-MM-dd'"> </datepicker> 

    语言选择(默认是英文)

    导入语言资源文件,然后再设置:language

    设置成中文

    
    

    日历

    链接

    个人资料

    蓝蓝设计的小编 http://www.lanlanwork.com

    存档