通过分析市场上的竞品,能够让设计师在短时间内快速了解自己所做产品在整个市场中的定位。
凯励程APP设计规范
设计规范应用范围
1、设计规范适用于凯励程APP6.1.0版本
2、设计尺寸750*1334
3、规范字体为平方,单位px
为什么要对设计规范进行分析
1、方便多个设计师进行协同设计,把控视觉统一性,提率,减少返工率
2、提高开发效率、保证开发结果与视觉效果图的还原度
3、辅助设计及开发理解业务
4、方便产品迭代
LOGO设计
凯励程logo设计的运用元素:首字母k的变形+盾牌外轮框组合设计,logo设计运用名称的首字母作为元素给人的感觉一目了然,凯励程的核心功能是专注汽车安全服务,所以这里的盾牌比较有安全感,颜色使用深色调,稳定内敛。
App界面的整体风格扁平,颜色搭配清爽,首页通过卡片式布局将各功能及信息模块分区展示,寻车功能为整个app的核心功能,所以将这块区域放置在首页的banner位置,并且加了圆角和投影,层次鲜明,使人的视觉会在第一时间浏览此功能,查看车辆的位置情况。icon采用线性icon和部分实心icon相结合,图标简洁清晰易识别,色调统一,明度一致。
颜色搭配有互补色和同类色,互补色为色相环上,夹角互为180度的色彩,互补色具有强烈的对比;同类色为色相环上,夹角为60度以内的色彩,色相对比差异比较小,给人以协调统一、稳定自然的印象。同类色的搭配也很容易出画面效果,不太容易出错。通常为了避免版面呆板,可以通过增加明暗对比,来制造出丰富的质感和层次主色调为蓝色,辅助色为浅蓝色。
文章来源:站酷
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
Flutter 构建模式
目前,Flutter一共提供了三种运行模式,分别是Debug、Release和Profile模式。其中,Debug模式主要用在软件编写过程中,Release模式主要用于应用发布过程中,而Profile模式则主要用于应用性能分析时,每个模式都有自己特殊的使用场景。下面简介介绍下这几种模式:
Debug模式
Debug模式又名调试模式,Debug模式可以同时在物理设备、仿真器或者模拟器上运行应用。默认情况下,使用flutter run命令运行应用程序时就是使用的Debug模式。在Debug模式下,所有的断言、服务扩展是开启的,并且在模式对快速开发和运行周期进行了编译优化,当使用调试工具进行代码调试时可以直接连接到应用的进程里。
Release模式
Release模式又名发布模式,此模式只能在物理设备上运行,不能在模拟器上运行。使用flutter run --release命令运行应用程序时就是使用的Release模式。在Release模式下,断点、调试信息和服务扩展是不可用的,并且Release模式针对快速启动、快速执行和安装包大小进行了优化。
Profile模式
Profile模式只能在物理设备上运行,不能在模拟器上运行。此模式主要用于应用性能分析,一些应用调试能力是被保留的,目的是分析应用存在的性能问题。Profile模式和Release模式大体相同,不同点体现在,Profile模式的某些服务扩展是启用的,某些进程调试手段也是开启的。
调试模式
在 Debug 模式下,app 可以被安装在物理设备、仿真器或者模拟器上进行调试。在Debug模式下,可以进行如下操作:
断点 是开启的。
服务扩展是开启的。
针对快速开发和运行周期进行了编译优化(但不是针对执行速度、二进制文件大小或者部署)。
调试开启,类似 开发者工具 等调试工具可以连接到进程里。
如果是在 Web 平台下的调试模式,可以进行如下操作:
本次构建 没有 最小化资源并且整个构建 没有 优化性能。
为了简化调试,这个 Web 应用使用了 dartdevc 编译器。
默认情况下,运行 flutter run 会使用 Debug 模式,同时 IDE 也支持这些模式。例如,Android Studio 提供了 Run > Debug… 菜单选项,而且在项目面板中还有一个三角形的绿色运行按钮图标 。
Release 模式
当你想要最大的优化以及最小的占用空间时,就使用 Release 模式来部署 app。 release 模式是不支持模拟器或者仿真器的,使用 Release 模式意味着。
断点是不可用的。
调试信息是不可见的。
调试是禁用的。
编译针对快速启动、快速执行和小的 package 的大小进行了优化。
服务扩展是禁用的。
对于Web开发来说,使用 Release 模式意味着。
这次构建资源已经被压缩,并且性能得以优化。
这个 Web 应用通过 dart2js 编译器构建,以确保更优秀的性能。
Profile 模式
在 Profile 模式下,一些调试能力是被保留的,足够分析你的 app 性能。Profile 模式在仿真器和模拟器上是不可用的,因为他们的行为不能代表真实的性能。和 release 相比, profile 模式有以下不同:
一些服务扩展是启用的。例如,支持 performance overlay。
Tracing 是启用的,一些调试工具,比如 开发者工具 可以连接到进程里。
在 Web 平台使用Profile 模式意味着:
资源文件没有被压缩,但是整体性能已经优化。
这个 Web 应用通过 dart2js 编译器构建。
调试工具
在Flutter应用开发中,有很多工具可以帮助调试 Flutter 应用程序,常见的如下所示。
开发者工具,是一套运行在浏览器的性能及分析工具。
Android Studio/IntelliJ 和 VS Code(借助 Flutter 和 Dart 插件)支持内置的源代码调试器,可以设置断点,单步调试,检查数值。
Flutter inspector,是开发者工具提供的 widget 检查器,也可直接在 Android Studio 和 IntelliJ 中使用(借助 Flutter 插件)。检查器可以可视化展现 widget 树,查看单个 widget 及其属性值,开启性能图层,等等。
开发者工具
要调试及分析应用,开发者工具可能是你的首选。开发者工具运行在浏览器,支持以下特性:
源代码调试器
Widget 检查器,展示可视化的 widget 树; “widget select” 模式,在应用中选择一个 widget,会在 widget 树直接定位到它的位置。
内存分析
时间线视图,支持跟踪,导入及导出跟踪信息
日志视图
如果你在Debug 模式 或Profile 模式 运行,那么可以在浏览器打开开发者工具连接到你的应用。开发者工具不能用在 Release 模式 编译的应用,因为调试和分析信息都被删除了。如果你要用开发者工具分析应用,需确保使用 Profile 模式运行应用。
在这里插入图片描述
断点调试
和其他语言一样,Flutter的断点调试支持在 IDE 或编辑器(比如 Android Studio/IntelliJ 和 VS Code)、或者通过编码两种方式。
其中,开发者工具调试器如下图所示。
在这里插入图片描述
如果需要,在源代码中设置断点,然后点击工具栏中的 【Debug】 按钮,或选择 【Run】 > 【Debug】即可开启调试功能。
在这里插入图片描述
开启调试后,可以在控制台看到如下一些信息。
底部的 Debugger 窗口会显示出堆栈和变量信息。
底部的 Console 窗口会显示详细的日志输出。
调试基于默认的启动配置,如果需要自定义,点击选择目标下拉按钮,选择 Edit configuration 进行配置。
在进行断点调试时,使用得最多的就是单步调试,三个单步调试按钮在暂停后会变为可用状态。
使用 Step in 来进入被调用的方法,在遇到方法内的第一行可执行代码时结束。
使用 Step over 直接执行某个方法调用而不进入内部;该按钮在当前方法内按行执行。
使用 Step out 来跳出当前方法,这种方式会直接执行完所有当前方法内的语句。
除此之外,我们还可以使用代码的方式进行断点调试,我们可以在源代码中使用 debugger()函数来开启断点,当代码运行到此处时就会刮起,如下所示。
import 'dart:developer';
void someFunction(double offset) {
debugger(when: offset > 30.0);
// ...
}
Dart 分析器
如果你使用的是 Android Studio或者VSCode,那么工具会自带的 Dart 分析器默认会检查代码,并发现可能的错误。如果你使用命令行,则可以使用 flutter analyze命令来检查代码。Dart 分析器非常依赖你在代码中添加的类型注解,以帮助跟踪问题。
另外,我们可以使用flutter analyze --flutter-repo命令将分析结果打印到控制台上,每次运行这个命名之前,请先运行flutter update-packages 升级的包,这样就可以获取的依赖包。如果你不这样做,你可能会从dart:ui得到一些错误消息,比如偏移量等。因为执行flutter analysis 命令时并不会主动去拉取依赖。
对于一次性的Dart分析,直接使用flutter analyze --flutter-repo即可,对于连续分析,则可以使用flutter analyze --flutter-repo --watch命令。如果你想知道多少个成员变量丢失了dartdocs,可以添加一个dartdocs参数。
Flutter inspector 工具
Flutter inspector 是分析Flutter组件状态树的利器,Flutter使用小部件来控制页面组件到布局的精准控制,Flutter inspector 可以帮助我们进行如下一些分析。
进行布局分析,理解布局层次
诊断布局问题
在这里插入图片描述
在调试模式下,我们点击Android Studio右边Flutter inspector按钮即可开启Flutter inspector分析,Flutter inspector提供了如下的可视化调试工具。
在这里插入图片描述
Select widget mode:启用此按钮后,选择组件树的代码会自动跳转到对应的源代码里面。
Refresh tree : 重新加载的组件信息。
Slow Animations:放慢动画速度,以便进行视觉上的查验。
Debug Paint: 边框、方向的可视化。
Paint Baselines: 每个渲染框在它的每个文本基线上画一条线。
Repaint Rainbow:查看重绘的严重程度,严重的会被爆红。
除了上面的功能外,我们还可以点击【Open DevTools】打开Flutter的调试页面,可以借助它进行很多性能分析,后面会具体介绍。
在这里插入图片描述
测量应用启动时间
要收集有关 Flutter 应用程序启动所需时间的详细信息,可以在运行 flutter run 命令时使用 trace-startup 和 profile 选项,如下所示。
flutter run --trace-startup --profile
跟踪输出被保存到 Flutter 工程目录在 build 目录下,一个名为 start_up_info.json 的 JSON 文件中,输出列出了从应用程序启动到这些跟踪事件(以微秒捕获)所用的时间,如下所示。
{
"engineEnterTimestampMicros": 2346054348633,
"timeToFrameworkInitMicros": 812748,
"timeToFirstFrameRasterizedMicros": 1573154,
"timeToFirstFrameMicros": 1221472,
"timeAfterFrameworkInitMicros": 408724
}
对应的具体含义如下:
进入 Flutter 引擎时
展示应用第一帧时
初始化Flutter框架时
完成Flutter框架初始化时
使用Android Studio进行调试
Flutter官方推荐使用Android Studio或VSCode进行应用开发, 和其他语言的调试一样,Dart代码的调试流程也差不多。如果还没有Flutter项目,可以新建一个示例项目。通过单击首先,点击调试图标(Debug-run icon)同时打开调试面板并在控制台中运行应用,首次运行应用是最慢的,应用启动后,界面应该是下面这样的。
在这里插入图片描述
然后,我们在在 counter++ 这一行上添加断点。在应用里,点击 + 按钮(FloatingActionButton,或者简称 FAB)来增加数字,应用会暂停。
在这里插入图片描述
你可以 step in/out/over Dart 语句、热重载和恢复执行应用、以及像使用其他调试器一样来使用 Dart 调试器。
Flutter inspector
Flutter inspector 是一个用来可视化以及查看 Flutter widget 树的工具,提供如下功能:
了解现有布局
诊断布局问题
可以使用 Android Studio 窗口右侧的垂直按钮来打开Flutter inspector,如下图所示。
在这里插入图片描述
Flutter outline
Flutter Outline 是一个可视的显示页面构建方法的功能,注意在构建方法上可能与 widget 树不同,可以使用 Android Studio 窗口右侧的垂直按钮切换 outline 的显示。
在这里插入图片描述
Tip: 我们可以安装一个 Presentation Assistant 插件来辅助我们进行开发,Presentation Assistant 提供了很多的快捷功能。例如,当焦点在编辑面板中时,输入 command-Shift-A(Mac)或者 shift-control-A(Windows 和 Linux),该插件会同时显示「查找」面板并显示在所有三个平台上执行此操作的提示。
在这里插入图片描述
然后在输入框中输入attach关键字,显示如下图。
在这里插入图片描述
使用 Android Gradle 调试
为了调试原生代码,你需要一个包含 Android 原生代码的应用。在本节中,你将学会如何连接两个调试器到你的应用:
1)Dart 调试器。
2)Android Gradle 调试器。
创建一个基本的 Flutter 应用,然后替换 lib/main.dart 的代码为以下示例代码。
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'URL Launcher',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'URL Launcher'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<void> _launched;
Future<void> _launchInBrowser(String url) async {
if (await canLaunch(url)) {
await launch(url, forceSafariVC: false, forceWebView: false);
} else {
throw 'Could not launch $url';
}
}
Future<void> _launchInWebViewOrVC(String url) async {
if (await canLaunch(url)) {
await launch(url, forceSafariVC: true, forceWebView: true);
} else {
throw 'Could not launch $url';
}
}
Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text('');
}
}
@override
Widget build(BuildContext context) {
String toLaunch = 'https://flutter.dev';
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.all(16.0),
child: Text(toLaunch),
),
RaisedButton(
onPressed: () => setState(() {
_launched = _launchInBrowser(toLaunch);
}),
child: Text('Launch in browser'),
),
Padding(padding: EdgeInsets.all(16.0)),
RaisedButton(
onPressed: () => setState(() {
_launched = _launchInWebViewOrVC(toLaunch);
}),
child: Text('Launch in app'),
),
Padding(padding: EdgeInsets.all(16.0)),
FutureBuilder<void>(future: _launched, builder: _launchStatus),
],
),
),
);
}
}
然后,添加 url_launcher 依赖到 pubspec 文件,并执行 flutter pub get命令拉取依赖包。
name: flutter_app
description: A new Flutter application.
version: 1.0.0+1
dependencies:
flutter:
sdk: flutter
url_launcher: ^3.0.3
cupertino_icons: ^0.1.2
dev_dependencies:
flutter_test:
sdk: flutter
点击调试按钮(Debug-run icon)来同时打开调试面板并启动应用,如下图所示。
在这里插入图片描述
点击 【Attach debugger to Android process】 按钮,从进程对话框中,你应该可以看到每一个设备的入口。选择 show all processes 来显示每个设备可用的进程。
在这里插入图片描述
在调试面板中,你现在应该可以看到一个 Android Debugger 标签页,然后依次选择【app_name】 > 【android】 > 【app】 > 【src】 >【 main】 > 【java】 > 【io.flutter plugins】在项目面板,然后双击 GeneratedProjectRegistrant 在编辑面板中打开 Java 代码,此时Dart 和原生调试器都在与同一个进程交互。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
构图、色彩和光影属于设计中非常重要的3块体系,但构图和色彩大多是偏感性的主观理解,而光影则是理性的客观判断。因为这是自然界中真实存在的物理现象,因此大部分时候画面的「添加光影」都是在还原真实,所以本文的很多知识都偏理论和科普,但理性认知无疑是打牢光影基础的第一步。
如果说「构图」和「色彩」是视觉设计的基础,那光影便是「锦上添花」,当各种视觉元素组合完成后,只有「光影统一」,才能让所有元素真实的融在一起,最终形成真实立体、有层次的画面,而「光影统一」便是这篇文章的核心原则。
其实我们看到的万物色彩,都是物体表面的反射光,是「光」创造了色彩,同时也创造了「影」,只有经过光照才会出现明暗,才有所谓的「光影」,所以我们先来说说一切的源头——光。
发光的地方就是「光源」,当我们对画面「添加光影」时,一定要先留意光源在哪,像我每次画设计草图时,就会先把主光源的位置确定并标示出来,只有清楚位置,「光影统一」才有了依据。
那常用的光源类型都有哪些呢?其实就2大类:照射光和环境光。2类光源往往同时存在,相互影响,塑造场景时必须同时考虑。
照射光就是画面中的主要照明光,这是影响整体光影的核心光源,根据光线照射路径的不同,又分成直射光、散射光和折射光。
直射光
直接照射的光源就是直射光,光线路径呈方向明确的两点一线,整体集中、聚焦。
直射光能在物体表面形成强烈的明暗对比,并且过渡偏硬,形成清晰的轮廓边缘,最常见的直射光就是晴天阳光,它属于自然光。如下图所示,阳光直射形成的明暗强对比能呈现人物及建筑的轮廓,突显立体感。
和自然光相对应的是人造光,在人造光中,例如摄影棚里常用的闪光灯和常亮灯(不带柔光罩)都算直射光,照射效果和晴天阳光类似,仔细观察投影,边缘都很生硬、清晰。
散射光
当光束穿过某一介质(云层、柔光布等),被其表面分散传播的光便是散射光,散射光的光线路径呈多方向发散状,整体分散、无明确方向。
自然光中,阴天、雨天、雾天的光照都属于散射光,当阳光穿过大气时,大气层能让光线朝不同方向发散,发散后的光线柔和,在物体上形成的明暗对比较弱,过渡也柔和。如下图所示,不管人物还是景物,都无明显的阴影轮廓,整体层次丰富而细腻,影调柔和,特别是暗部的细节都能保留完好。
而人造光中,如果给闪光灯和常亮灯装上柔光罩,这时发出的光就会变成散射光。例如下图中,人物和产品都显的特别柔和、舒服。
折射光
当光束从一个介质射入另一介质时,传播方向发生偏折的光就是折射光。常见介质有水、玻璃等,它们的折射角度也各不相同。
日常生活中,像泳池里的水波光影、玻璃杯投射的光线图案都是与折射相关的自然现象。
而我们在设计Banner时,若将这些折射效果表现出来,就会给画面增添很多细节,同时也会增加真实感,像水波光影在电商中用的就很多。
很多新手在做设计时,往往只注意照射光(主光源)对物体的影响,而忽略环境光,导致画面总是不够真实。简单来说,画面中除了主光源外的所有光线都算环境光,环境光的亮度一般很低,且没有明确方向性,常见的环境光有2种:散射光和反射光。
散射光
和照射光一样,环境光中也有散射光,但它们的不同在于:
像自然界中的夕阳西下,当红色的太阳光(照射光)减弱时,建筑的暗部便会显现蓝色的天空光(环境光),这时红光和蓝光会形成鲜明对比,极具冲击和美感,这样的画面在摄影及设计中都非常常见,也是摄影界公认的「黄金一小时」,这时的天空光就属于环境光中的散射光。
生活中还有一个常见现象也能看到散射光影响,当你走在户外,观察自己的影子,特别当影子较长时(清晨或傍晚),这时离自己越远的区域颜色越浅,这是因为越远的影子所处区域就越开阔,受周围散射光的影响就越明显,因而颜色更浅。
反射光
环境光中还有非常重要的一类是反射光,当光束射到介质表面时,有部分自介面射回的光就是反射光。可以毫不夸张的讲,世界万物之所以出现明暗就是因为反射光的存在。反射光一般有2种情形:镜面反射和漫反射。
镜面反射。当反射面非常光滑,这时平行射入的光线仍会向一个方向平行的反射出来,效果如同镜子一样,虽然这样物体本身的明暗就会非常微弱,但人们透过反射面能看到周围的环境信息,这就是镜面反射光。
日常生活中像平静水面、镜子、抛光金属等都会有镜面反射光,例如我们在刻画金属材质的元素时,常常会在表面加些近乎白色的高光,这就是由于镜面反射而进入眼中的刺目强光。
另外晴空万里时,海面也常出现「波光粼粼」的闪光,同样也是阳光射入水面的镜面反射光。
漫反射光。当反射面凹凸不平时,这时平行射入的光线就会向各个方向反射出去,这便是漫反射光(以下简称「漫射光」)。需要说明这里的凹凸不平也包括微观结构,例如有些墙壁看着光滑,但仔细看表面也有粗糙颗粒,这时反射出来的光线也是各个方向的漫射光。
漫反射在这个世界里无处不在,它是我们对物体形态及色彩产生明确认知的基础。
因为有漫反射的存在,物体才会有清晰的明暗关系,我们才能看清这个世界,它赋予了物体纵深感和体积感,不管是产品还是环境皆是如此。
这里列举一个设计中常用的漫反射现象,我们将一个白色茶杯放入一个绿色盒中,打上一束光,这时茶杯整体都带有绿色调,这是因为盒子表面产生了大量绿色的漫射光,这些光束射在茶杯上,进而发生偏色现象,如下图所示。
可见环境色对物体的视觉影响还是相当明显。再如下图中,当模特处于一个红色空间时,受到环境中红色漫射光的影响,模特也明显偏向红色,这时我们就说环境光影响了这位模特的「色彩平衡」,关于「色彩平衡」在后面还会细讲。
当然现实是复杂的,其实还有大量物体会同时存在镜面反射光和漫射光,谁的强度大谁就更突出,例如起风时的水面,就不全是镜面反射,还有漫反射,这里就不再深究。
小结
以上提到的「照射光」和「环境光」便是设计中常用的2大光源类型,这2类往往是同时存在,一般照射光会直接影响物体的明暗结构,而环境光则影响物体的色彩平衡及反光。
只有把这些光影都表现清楚,画面才更有代入感。其中对于照射光的理解,绝不仅仅是考虑直射或散射这么简单,还需分析照射方向、照射角度、照射形状、光源距离、光源强度、光源软硬、光源大小、光源颜色等因素,待会会针对这些因素展开讲解。
前面介绍了光源类型,主要让大家对「光」有个整体认知,内容偏科普,属于「光影」的理论基础,现在开始讲「影」,这块内容更偏实战运用,教大家如何在设计中准确表达光影。本文的「影」涵盖两块内容:物体的明暗和投影,其中物体是泛指,包含电商常用的两大元素:人物和产品。
先说物体明暗,当光源发出的光线射向物体时,由于光反射,物体会有对应的明暗变化,记得在高中学习素描时,老师就曾提过光影的「五大调」,这正是人们对物体明暗关系的理论化总结。
但现实世界却要复杂的多,因为物体光影还与材质密切相关,不同材质的明暗关系截然不同,所以决不能撇开材质谈明暗,而设计中常用材质有三大类:漫反射材质、镜面反射材质和透明/半透明材质。
不管现实世界还是电商视觉,漫反射材质都是最常见的材质,因为这类材质的光影最有规律也最有代表性,明暗关系也简单很多,刚刚说的「素描五大调」就是针对漫反射材质,那我们就从这类材质入手,详细讲讲漫反射的光影到底如何呈现。
「漫反射材质」是指表面产生漫反射光的材质,日常生活中,漫反射材质(以下简称「漫射材质」)的物体占据大多数,像棉布、哑光纸、哑光塑料甚至人类肌肤等等都属于漫射材质。
而我们在刻画漫射材质的物体明暗时,需遵循3点原则:近亮远暗、先整体再局部以及细节刻画。
近亮远暗
对于漫射材质,首先一个大原则就是「近亮远暗」:
以画面的主光源(照射光)为圆心,物体距离光源越近会越亮,越远则越暗。
对单个物体来说,距离近的就是亮面(受光面),而距离远的是暗面(背光面),如下图所示。
在很多摄影或设计作品中,能看到不管人物还是产品,都会遵循这一原则:距离主光源越远则越暗。
先整体再局部
在「近亮远暗」的大原则下,先确定物体的整体光影,表现三大面(亮面、灰面、暗面),其本质就是在固有色(物体在白色光下所呈现的色彩)的基础上进行深浅色调的变化。
然后再添加物体的局部光影,这个局部主要针对有块面的物体(立方体),「局部光影」意味着亮面和灰面也要分别遵循「近亮远暗」原则(由于暗面是背光,不受主光源影响,所以不在遵循范围内),这样光影才会更有层次。而没有明显块面的曲面物体(球体)则把握好整体光影即可,或许听着有些复杂,下面通过图例强化理解。
细节刻画
最后是刻画光影细节,这里列出2个常见细节,这些细节虽不影响整体明暗,但会让物体光影更加细腻和真实,属于「加分项」。
物体表面最亮的地方就是高光,高光其实不是光,而是直接反射主光源的地方,如果要给漫射材质的球体添加高光,那在亮面添加一个羽化的圆斑即可。
往往越光滑的物体高光就越清晰。但对漫射材质而言,高光不会很清晰,不过模糊程度要看物体的固有色以及粗糙度,粗糙度越大的物体高光越模糊。另外高光的外形还和物体的本身结构有关。
在表现高光时,还有一块高光区也经常被刻画出来,那就是块面物体的「倒角高光」。
「倒角」其实是个工业设计术语,三维设计也常提到,一般块面物体的棱角通常会做些圆滑过渡,这种过渡结构就是倒角,有了倒角,转折才不会「锋利」,这时若有光线照射到表面,倒角处便会形成高光线。
一般亮面和灰面转折处的倒角高光最亮,而灰面和暗面的转折处最暗,核心还是遵循「近亮远暗」原则。
电商设计中,若给块面物体加上「倒角高光」,细节无疑更丰富,也更耐看。仔细看下图,在块面转折处都有明显的亮线勾勒,虽然不是很起眼,但这就是常说的「设计细节」。
当2个物体相邻时,它们相邻的那面会有「重合阴影区」,并且物体离的越近,阴影会越深。这是因为相邻空间随着物体间距越小,接受的环境光也会越来越少。
「重合阴影」是一个非常容易被大家忽略的设计细节,但若表现得当,就能提升作品的精细度,如下图所示。
说完漫射物体的明暗刻画原则,接下来讲讲周围环境对物体的影响,还记得刚刚讲过的环境光吗?环境中除了主光源外的一切光线都是环境光。
而「环境色」则是环境光中的一种情形:就是当周围环境有明确色彩时,这时产生的环境光会给物体带来怎样的影响?主要影响其实有2方面:物体的色彩平衡和反光,实际表现时也是从这两点入手。
色彩平衡
「色彩平衡」是PS中的一项调色工具,主要是调节画面的整体色彩。既可校正画面的偏色,使色彩舒适平衡;也可反其道而行之,根据场景和需求让画面有意偏向某种颜色。例如下图中的人物,受环境色影响就明显偏向黄色。
所以当物体处在一个有明确色彩的环境时,受环境四周的漫射光影响,整个物体都会偏向环境色,注意由于暗面受到的影响更大,因此暗面的偏色现象也会更加明显。总之物体偏色程度是和其固有色、材质及漫射光强度都息息相关。
反光
一般物体都是放置在地面上,而地面作为环境的一部分也会出现漫射光,这部分光线射到物体上就会形成一层微弱的亮面,这就是「反光」。
其中物体表面离地面越近的地方反光就越强,一般来说反光最强处就是物体暗面最靠近地面的地方。但不管如何反光都不宜过亮,更不能超过物体亮面。
上图是环境为白色的情形,而当环境有明确色彩时,这时反光面就应呈现环境色,如下图所示,物体的反光都是浅蓝色。
另外能产生「反光」的不单单是地面,其实只要离物体较近且能反射光线的面都能让物体产生「反光」。
例如下图中,就能明显看到白色瓶子的右侧有一层绿色「反光」,这是因为旁边的绿色外盒反射出的绿色光线射在了瓶身表面。
再如户外拍摄人像时,有时为了不让暗部过暗,往往会在旁边添上一块「反光板」,这个反光板所起作用就是让脸部的背光面产生反光,以达到提亮暗部的目的。
小结
综上所述,周围环境的漫射光影响着物体的色彩平衡;而地面(也可是离物体较近的面)的漫射光则为物体暗面添加了反光。
为何要单独讲解环境色影响?因为现在很多Banner都是在一个有色背景中添加人物或产品,其实就可理解成是将物体放入一个环境色中,这时若想和背景自然融合,就需要它们的色彩平衡及反光都偏向背景色,不然整体就会失真。
总之调节「色彩平衡」能让物体融进有色背景中;而添加「反光」可使物体更加通透,体积感也更强,下面展示2个融合不错的案例。
在实战案例中,我们会给2款产品分别添加一个蓝色背景,然后用 「明暗原则」及「环境色影响」中提到的方法给产品加上光影,并将它们融进背景中(由于投影在后面才会提到,所以当前为保画面完整性,关于投影部分只先添加,但不展开细讲)。因为物体的明暗、投影和光源属性密切相关,因此这里先设定主光源来自画面左上方、强度中等、软硬适中、白光,下面看具体如何呈现。
在上述案例中,我们事先给主光源做了一个设定,为何要这么做呢?因为主光源的很多因素都直接决定了物体会呈现怎样的光影,特别当画面有多个物体时,只有确定了光才能使它们「光影统一」。
例如刚刚通过左上角的主光源能判断画面属于侧光,那产品也是对应的侧光影,如果位置改变那光影也将发生改变,那光源的众多因素究竟会让物体产生哪些不同?下面一一细说。
照射方向
先说光源的照射方向,主要会影响物体的「明暗配比」,随着方向不同,物体明暗也在发生微妙的变化,同时还会影响画面的情感表达,所以照射方向是我们首先就要确定的因素,一般方向有7种。
而在实际运用时,常用方向是4种:前侧光、侧光、侧逆光、逆光,这4种我们分成2组来讲。(其他方向由于使用较少,就不展开)
前侧光/侧光。当光源的照射方向和视线方向成30°-60°夹角时称为前侧光;而当夹角成90°时则为侧光。如图所示,前侧光一般明多暗少;侧光则是明暗对半。
下面再看产品在前侧光及侧光下的光影呈现,主要区别在于明暗比例的变化,但变化很微妙,区分没有那么明显。这里我是用PS对产品进行的光影调整,因为日常工作中,PS处理光影才是大家的常用方法。
前侧光及侧光是电商设计中最常用的布光方式,这样不管人物还是产品,明暗比例都比较适中,既能保证物体的亮度,也能很好的凸显物体形态和质感,所以大量Banner的主体展示都会采用这种布光方式,下面看案例。
侧逆光/逆光。当光源的照射方向和视线方向成120°-150°夹角时称为侧逆光;而当夹角成180°时则为逆光。如图所示,侧逆光和前侧光刚好相反,物体是明少暗多;而逆光时的物体则几乎全是暗面。总体来说,不管侧逆光还是逆光,物体都以暗调为主。
再看看产品在侧逆光及逆光下的光影该如何处理,虽然产品看着有些灰暗,但场景却颇有氛围和调性。
这也是一组常见的布光方式,和前测光/侧光主要凸显物体的立体感不同,侧逆光及逆光主要是营造独特的场景氛围。
其中要特别注意物体背光面的暗调程度,根据需求提亮或压暗,但多数时候都不会调的太暗,还是会保留物体该有的一切细节,避免色调过深而丢失了暗部层次,如下图所示,元素的背光面依然都清晰可见。
逆光其实很有趣,当光照强烈、光质偏硬时,物体边缘就会出现一圈非常明显的高光,高光颜色和光源颜色一致,这就是「轮廓光」。光照强度越大、光质越硬,轮廓光就越明显。
轮廓光一直都是摄影师的最爱之一,如图所示,它能勾出人物轮廓,进而分离人物和背景,让影调富有变化,提升画面层次和细节。
在电商设计中也同样常见,例如下图中,仔细观察人物和产品的边缘,有些地方会有一层非常高亮的「轮廓光」,这处光影细节使画面更具形式感和设计感。
还有一种情形也颇有艺术感,如果物体完全背光,这时背景偏亮而物体正面又无光时,就会形成「剪影」效果。在摄影中这是一种很有趣的拍摄方式,如图所示,图中的人物剪影会给观众留下丰富的想象空间。
在电商设计中,剪影同样以表现人物或产品轮廓为主,突出整体造型,「剪影构图」会更强调画面的形式感传达。
光源强度/距离
光源强度和距离会影响物体明暗的反差大小,强度越弱或距离越远则物体明暗反差越小;而强度越强或距离越近则明暗反差越大。
原因很好理解,先说光源强度,当光源增强时,周围环境和物体的受光面会更亮,因此亮面和暗面的反差也会更大。还有距离同样如此,所有人造光源的光照都有衰减性,而光源强度随着距离拉近而升高,因而距离越近的光源,照射强度同样增强。
需要注意,还有一类光源是自然光,自然光比较特殊,如果在室外环境下,任何时候的光照(晴天、阴天等)都不会有衰减现象,因为作为光源的太阳太强,距离太远,衰减可忽略不计。
但若在室内,阴天从窗外射入的自然光则会有衰减性,因为这时的光线以散射光为主,而这类光照要弱的多,再加上窗外射到室内的辐射范围有限,所以衰减性便会显现出来,如下图所示。
光源强度和距离对产品的明暗影响也同样如此:左图设定的光源强度弱、距离远,因而产品的明暗反差小;而右图设定的光源强度强、距离近,因而明暗反差更大。
如果选择明暗弱反差,为避免画面灰暗,我们需要提升画面的曝光值,使整体明亮。
在实际运用时,明暗弱反差降低了明暗对比,人物或产品都没有很深的阴影,整体呈现清晰、柔和,同时也弱化了结构和立体感,如图所示。
而明暗强反差则让人物或产品的明暗对比强烈,阴影明显,质感凸显,更强调整体结构和立体感,如图所示。
光源软硬
喜欢摄影的朋友对这个词应该再熟悉不过,是指光的性质(简称「光质」)变化,分成硬光和软光。
一般直射光属于硬光,而散射光和漫射光则属软光。光源软硬会影响物体的明暗过渡,硬光的光照直接,会让物体的明暗过渡更为生硬,有明显的阴影轮廓,突出表面结构和质感;而软光的光照柔和,能让物体的明暗过渡更为自然,无明显的阴影轮廓,突出表面层次和细节。
另外在同等的光照强度下,由于软光的光线呈分散状,所以相比硬光,明暗反差也相对较小,如下图所示。
下面再展示产品在硬光和软光下的光影刻画,明显左图的影调更为硬朗。
在设计时,硬光常用来表现人物的强劲、硬朗和力量,常用于男性、运动、健身等类目,另外也能凸显产品的结构和造型,提升质感和立体感。
软光则侧重表现人物的柔美、清新和娇嫩,常用于少女、儿童等类目;而用于产品则会让其表面的层次细腻而丰富,更接近生活里的真实呈现。
光源颜色
最后是光的颜色(简称「光色」),光色变化会影响物体的表面颜色,由于人们对色彩的敏感度很高,所以众多因素中,光色带来的影响最为直观。
一般来说,不管物体的固有色如何变化,表面呈现的都是光线颜色,只是明暗程度会有不同。如图所示,当红光照在球体上,亮面会呈现红色;而绿光照射则会是绿色。
若产品被有色光照射时,受光面同样会出现对应的光色,这时画面会更生动,同时也提升了用户的视觉印象。
在日常设计中,使用有色光算是「戏剧化用光」的一种手法,如图所示,当画面出现光色变化和鲜明对比时,画面会更有冲击和氛围,也让场景带有强烈的情绪感。
小结
以上便是影响物体明暗的4个光源因素:照射方向、光源强度/距离、光源软硬以及光源颜色。
通过相关案例,细心的小伙伴该会发现,当照射光的这些因素发生改变时,不单单是物体表面的明暗会有变化,其实投影也有明显不同,确实物体的明暗和投影都是紧密相关,正因为投影太过重要,所以接下来单独介绍。
前面提到本文的「影」会涵盖两块内容:物体的明暗和投影。现在就说说投影,我们还是从最具代表性的漫射材质入手。
何为投影?简单说就是光线照射不到的地方。它是光影表现中非常重要的一环,有了投影,环境中的物体才有真实感,并和环境产生呼应关系,给人带来现实感。
而投影呈现,就属于典型的看着简单其实复杂的细节刻画,新手往往觉得投影不就是添加黑色的模糊椭圆吗,偶尔虽然可行,但这并不适用所有场景。设计师真正要做的,是能根据各类场景准确表现出让人舒服的物体投影。而物体投影,又分为表面投影和地面投影,投在物体表面的是表面投影;而物体投在地面的就是地面投影。
刚刚讲「照射光影响」时提过,当光源强度、软硬等因素改变时,物体的投影也会有明显不同,那接下来我们就看看物体投影到底受哪些因素影响?又会有哪些不同?刻画时考虑的要素都有哪些?相信看完会刷新大家对投影的认知,原来看似简单的投影竟藏有这么多细节!
首先要考虑投影的方向,这是大前提,它和光源位置密切相关,核心原则是投影永远在光源相对的一面,属于光源光线的延伸。如图所示,当画面有多个物体时,要确保所有影子都和光源的光线方向保持一致,若不一致画面就有违和感。
下面看案例,注意有时画面的光源位置并不明显,会在画面外,例如右图,这时就要事先设定一个光源位置,然后确保所有物体的投影都处在光源光线的延伸线上,这样才合情合理。
确认方向后就要开始绘制投影的轮廓,这是投影表现中最难的一步,很多画面的投影看着很假往往就是轮廓出了问题,一个优秀设计师要能准确呈现出物体投在地面的真实形状,而不是所有投影都是一个圆形或矩形。那怎样才能准确的勾出外形呢?我们需从以下3点来考虑:基本外形、发散程度和外形起伏。
基本外形
是指物体在光源照射下投在平面上的基础形状,关于形状绘制有章可循,但需用到我们在高中「立体几何」中所掌握的空间感。
总体来说,是先把光源的「位置点」和物体的各个「顶点」连成直线,再把每条直线延长至所在平面,这样就能得到多个「交点」,最后把平面投影外围的所有「交点」连接起来便会得到准确轮廓,如下图所示。
上方是单个长方体的投影外形,可能这样的简单物体还比较好呈现,那复杂物体呢?其实用同样方法即可,如下图所示,我们先用上述方法将2个长方体的投影轮廓分别呈现,再合并就好。只是这时的空间更复杂,顶点也更多,我们要有足够的眼力和耐心。
需要说明,在单一光源下,当画面有多个物体组合出现时,所有物体的投影都是相加关系。即是说当多个投影有交集时,这些交集区域不会产生更深的叠加投影。
但以上只针对单一光源产生单一投影时的情况,若画面有多个光源并让物体产生了多个方向的投影时,此时投影便成叠加关系。
如下图所示,示意图和案例中都有2个主光源,因此物体产生了2个相交投影,投影的交集区域最深,而非交集区域由于光源的相互影响则会变浅。
最后通过投影基本形的绘制方法我们还能得出一个结论:影子长短和光源光线的入射角有关。入射光线和垂直地面的法线夹角就是入射角,入射角越大投影越长,入射角越小则投影越短。
准确的投影轮廓能让产品呈现更真实,也更有美感,下面展示2个优秀案例,当然这些的前提是要光源为硬光,只有硬光才会有清晰的投影轮廓。
发散程度
接着要考虑投影外形的发散程度,「发散」是说投影轮廓离物体越远则开口越大。所有物体的投影都有发散现象,只是程度不同,这和光源的面积大小及光源距离有关。
先说光源大小,光源面积越大则发散程度越小;而面积越小则发散程度越大。
再说光源距离,距离物体越远则发散程度越小;而距离越近则发散程度越大。
例如太阳距离地球就非常遥远,因此室外物体的影子扩散程度会非常小,像下图中的树木,投影都接近于平行。
其实距离远近是光源非常重要的一个分析维度,它影响的因素有很多,不光是投影发散,还会影响投影的深浅和虚实,后面再细说。
一般在电商设计中,扩散程度较小的投影用的更多,毕竟生活中这类投影更加常见,呈现出来的影子也会比较自然和真实。
扩散程度较大的投影虽然用的不多,但使用恰当则会让画面充满张力和氛围,使人眼前一亮,如下图所示。
外形起伏
投影的轮廓绘制还需考虑外形起伏,「起伏」是说投影不光要有二维平面的形状变化,还要根据地面凹凸进行纵向的起伏调整。
其中地面凸起主要指「墙面」;而地面凹陷则指「阶梯」;最后还有地面凹凸不平的「肌理」呈现。
当地面凸起形成类似「墙面」结构时,如果物体的影子长度大于墙面间距时,就会出现「投影上墙」现象,这是因为墙面也会出现一块光线照射不到的区域,如下图所示。
在设计时,如果投影刚好出现在产品和墙面的中间区域时,最好都设计成「投影上墙」的布光效果,这样两个元素间(产品和墙面)就会产生呼应和联动,整体感更强。
当地面凹陷出现类似「阶梯」结构时,如果物体的影子长度大于阶梯转角的间距时,就会出现「投影下沉」,如图所示。
这里要注意一个关键点,上图中的主光源出现在物体背面,属于侧逆光,这时「阶梯结构」受光照影响也会出现暗面,和投影一样,都属于光线照不到的区域,因此在阶梯转角的背光面,不会出现物体投影,有时设计师会顺手将物体投影叠加在转角暗面,其实是错误呈现。
我们在观察下生活中的真实投影,下方是我随手拍的一张屋顶照片,上午9点,栏杆在屋顶投下了长长的影子,可以清晰看到,圈中挡板的背光面并没有栏杆投影,就像被断开了一样。这是因为该区域都是背光面,不可能出现投影叠加的反常现象。
因此我们在设计时要额外注意,千万不要犯这种「投影叠加」的常识性错误,仔细观察下方作品中方块转角的背光面,都不会出现产品投影的叠加现象。
有时地面还会以「肌理」方式呈现,像常见的草地、水面、沙滩等等,如图所示,它们的表面都是凹凸不平,因此投影外形也要根据肌理起伏进行形态变化,这样才不会显得投影「太假」。
虽然投影的轮廓绘制我是从3小点依次展开,但实际设计时应该一气呵成,根据画面的光源同时确定投影外形、发散程度以及起伏,最终是为营造出物体在环境里的真实存在感。
有了投影的「形」,现在我们要确定投影的「色」。很多新手在添加投影时不管周围的环境色是什么,都会给影子直接填充黑色,最后导致影子在画面中格格不入,像是多余的存在。
其实投影呈黑色的情况非常少见,由于受到周围漫射光的影响,大部分时候都是跟着地面颜色走,这是总体原则。具体是会先给投影填上地面色,并将混合模式设成「正片叠底」,再将透明度调至合适数值即可。
仔细观察下方案例,投影并非「黑色」,都是深色调的地面色,这样投影才不至于突兀。
既然投影都是深色调的地面色,那到底「多深」才合适呢?这就需要我们调整投影的深浅。关于投影深浅,需从2个维度来调整:整体深浅和相对深浅。
整体深浅
整体深浅是指投影的整体明暗,和上节讲的「物体明暗」一样,都是受光源强度和距离的影响。其中「深浅」是说环境明暗的反差大小,因此所谓的「投影深」其实是由于环境的明暗反差很大,反之亦然。
如下图所示,光源强度越强则投影的明暗反差越大(投影深);而光源强度越弱则投影的明暗反差越小(投影浅),因此投影的整体深浅是相对周围环境而言的。
另外整体深浅还受光源的距离影响,由于光线有衰减性,光源距离越近则投影的明暗反差越大,而距离越远则投影的明暗反差越小。例如下方案例中,明显左图的明暗对比更强,投影更深。
相对深浅
相对深浅是指投影自身的明暗变化,即是说投影本身的明暗分布并不一致,哪怕整体很深的投影,但本身还是会有相对的深浅变化,具体则看投影所处区域的开阔程度:开阔程度越小投影越深;而开阔程度越大则投影越浅。
一般情况下,靠近物体底部的区域开阔程度最小,接受的环境光(散射光/反射光)也最少,所以投影最深;而离物体最远的区域开阔程度最大,能接受的环境光也最多,所以投影最浅。
综上所述,整体是遵循着「近深远浅」的原则,投影靠近物体的区域更深;而远离物体的区域更浅。该理论看似复杂其实简单,下面我再用一张示意图进一步说明。
若要表现投影的「相对深浅」,我们要重点呈现3个区域:
这3个区域会沿着投影轮廓呈直线分布,如下图所示,在实际设计时,投影的暗角区往往是单独的薄薄一层(作为点缀,面积不能太大),而本影区和半影区则会合并成另一层(色调由深到浅的渐变层),这样刻画产品投影时就是用这2个图层来呈现。
再看电商作品中,物体的投影刻画也是分2层呈现,注意物体底部的暗角区色调最深。
投影呈现的最后还要调整「虚实」:「虚」是说投影的边缘模糊;而「实」是说投影的边缘清晰。和深浅一样,虚实也分整体虚实和相对虚实。
整体虚实
投影的整体虚实是和光源软硬及距离有关,先说光源软硬,上节曾说过「光源的软硬会影响物体的明暗过渡」,那现在还可加一点,光源的软硬同时也影响着物体的投影虚实。
当光源为硬光时,光照直接,这时投影整体偏实、边缘清晰、过渡生硬;而当光源为软光时,光照柔和,光线分散,这时投影整体偏虚、边缘模糊、过渡柔和。
除了光源软硬,还有光源距离,光源距离越近则投影越虚;而距离越远则投影越实。
这是因为光源的距离越近,相交的光线就越多,这时光线的相交区域也越大,如下图所示,这样投影便会形成更加柔和的边缘。
从上图能看到,光源的距离变化对物体的光影影响很大,不但影响了投影虚实,同时还决定了投影的扩散程度和整体深浅,这和我们之前讲的一致。
一句话总结:光源的距离越近,投影的扩散程度越大、明暗反差也越大、边缘则越模糊;而光源的距离越远,投影的扩散程度越小、明暗反差也越小、边缘则越清晰。
在实际运用时,「实影」会让产品的明暗过渡生硬,暗部细节缺失,对投影的轮廓要求也高,总体较难掌握,因此使用相对较少。但「实影」却有着更强的表现力和冲击力,形式更加鲜明,富有张力。
而「虚影」则用的更多,它让物体呈现的更加细腻和自然,由于投影的边缘模糊,所以对轮廓要求也低,无需非常精准,哪怕结构复杂的物体,也无需勾勒出具体形状,因此若对投影表现没有把握,可以优先「虚影」,易操作也易出效果。例如下方案例中,不管什么样的物体,在软光的照射下,投影都是「模糊一片」,就算轮廓异常也不易察觉。
相对虚实
投影除了整体的虚实变化,自身也会有相应的虚实过渡,这和「相对深浅」类似,整体遵循着「近实远虚」原则:投影离物体越近,则边缘越清晰;若离物体越远,则边缘越模糊。
注意虚实过渡其实是个非常柔和的变化过程。之所以出现「越远越虚」,是因为越远的区域受到的环境光(散射光/反射光)影响越大。
当然在软光照射下,有些画面也会忽视投影的「近实远虚」,将投影直接处理成整体模糊,其实也不太违和,因为「近实远虚」算是一个非常微妙的细节呈现,不影响全局,但若能做到,画面层次将更加细腻,如下图所示。
小结
以上就是关于投影呈现的5大要素:投影方向、轮廓、颜色、深浅及虚实。但要注意设计不是物理学,设计师也不只是为了还原现实,因此很多时候不用太较真。例如有些画面的光源并没那么明确,这时投影有些地方刻画的差不多即可,出现一点失真也没关系,关键是不要让人觉得画面别扭和违和。
实战案例
还记得前面讲「明暗原则」时画的立方体和球体吗?下面我们用刚刚讲的知识给它们逐步加上投影,物体只有同时加上明暗和投影,才算真正融进了环境里。首先还是先明确光源情况:来自画面的左上方,强度中等,软硬适中,白光。
在刚刚「物体明暗」的实战案例中,我们曾给2款产品添过「物体明暗」和「投影」,但投影并未细讲,现在再逐步剖析一下产品的投影刻画,其实和刚刚讲的立方体如出一辙,同样先设定好光源:来自画面左上方,强度较大,软硬适中,白光。
前面展示的都是相对简单的示意案例,现在将以2款产品作为主视觉,分别用侧光和逆光来设计2张不同风格的Banner,通过完整案例让大家了解两种布光方式的不同以及物体对应的光影刻画。
通过2个综合案例,大家能看到不同光源所带来的感受也会不同:侧光使物体呈现的更加真实和立体;而逆光则营造出一种独特的场景氛围,因此我们要根据需求选择合适布光。
另外刻画投影时需要考虑的因素也有很多,例如影子的轮廓、深浅及虚实等等,但不用生搬硬套,还是前面那句话:核心是不要让人觉得别扭与违和。
又又是一篇很长的文章,信息量很大,我们围绕「光影」依次讲了光源类型、物体明暗以及物体投影,当然都是从漫反射材质入手,相对简单也有代表性,其实常见材质中还有镜面反射材质和透明/半透明材质,而这2类则要复杂很多,也较难呈现,这里就不展开。但不管材质如何变化,正如本文开篇所说,「光影统一」都是核心原则,什么样的光就会得到什么样的影,虽然听着简单,但当中要做的细节其实很多。
可能耐心读完,有些小伙伴会觉得很多内容过于理论和枯燥,好像不用这么麻烦也能做出差不多的「光影」,其实这种想法还是比较浅显的。「光影」和构图、色彩不同,它是现实中客观存在的一种现象,若表现过于感性就会导致画面违和、不真实,所以本文的大量内容都是在理性推导后得出的结论,总之「理性学习」是培养「正确设计感」的必经之路。最后为方便大家梳理逻辑和内容,下面附上本文的内容结构和知识框架。
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
编辑导语:导出功能我们经常能够使用到,看似一个很简单的功能,实则在设计的过程中需要考虑很多的因素。本文作者对导出功能的全过程进行了思考,为我们分析了两种解决方案,拆解了导出前和导出时的设计,并且进行了总结。
功能(百度释义):功能是一个汉语词语,拼音为gōng néng,意指事物或方法所发挥的有利作用;效能。
综合以上释义,我们再从产品的角度对其理解:功能的好坏不仅仅关乎其本身,更重要的要看是否为解决实际问题而服务。
那导出功能,解决了什么样的问题?我们先来看一个场景。
小A作为一名销售人员,需要每月向领导汇报一次销售情况,为了更直观的让领导看到销售情况,小A要对销售订单数据进行不同维度展示与分析。而目前系统只能查看订单列表与销售总额,不支持不同维度的数据分析。
从场景中我们可以看到这样一个问题,由于系统只能查看订单列表与销售总额,不支持更全面的数据分析,导致小A每次向领导汇报前,只能人工将一个月近5000笔的订单(此处不考虑后续不断增加的订单量)统计在excel中,从客户维度计算出销售额、销售量,完成客户维的销售情况分析。
从产品SKU维度计算出销售额、销售量,完成产品SKU维销售情况分析,导致小A的工作效率很低,同时人工抄录导致数据错误的的情况也常会出现,最终导致数据分析结果错误。
针对该问题,我们深入思考一下,小A想要进行更全面的数据分析,可以采用以下2种解决方案:
目前我们有两种解决方案可供选择,那我们采用哪一种解决方案会更好呢?
1)采用“新增销售数据分析页面”的解决方案
通过与小A的继续沟通发现数据分析的需求并不稳定,处于变化的状态。当依据小A的需求完成分析页面后,过了一段时间发现产品随着季节变化导致销售额也不稳定,从时间维度的分析数据没有参考价值,可能要舍弃这种分析方式。
此时我们发现,小A的需求可持续的时间极为短暂,不足以支撑一个长期存在的功能,但已经将其实现为功能,则意味着功能白做了或没有产生与成本对应的价值。
2)采用“导出销售数据到excel,通过excel的数据透视功能完成数据分析”解决方案
导出销售数据到excel,可以规避因手动抄录导致的数据分析结果错误问题,同时也会节省抄录数据的时间,提高小A工作效率。
数据导出后,小A可以通过excel的数据透视功能或者其他第三方软件完成定制化的数据分析,即使不开发数据分析功能,也可以满足小A的“更全面的数据分析”需求。
例如,当小A导出如下图所示的数据后:
小A第一步可以先完成不同月份的汇总,在表中添加“月份”字段,添加公式为“=MONTH(A2)”;
第二步:再点击表格中任意单元格–插入–透视表–新建工作表,将省份拖入数据透视表字段的【筛选器】,将月份拖入【行】中,将A、B、C、D产品销售额(元)拖入【值】中;将行标签改为月份,每个字段以求和方法计算并修改字段名称。
第三步:选中透视表中数据,调整格式为加千分位逗号,保留0位小数。
第四步:为了让数据展现可视化,插入透视图。
通过公式筛选、透视表、透视图的使用,小A可以点击查看不同地区、月份、产品的分类汇总数据,可以很直观地反映出时间维度的销售情况、不同地区的销售情况、很好地为产品选择、地域选择做出数据依据支撑。
数据分析功能会从数据源、数据处理、数据的可视化来实现,而Excel的透视表对应了数据明细(数据源),公式能够完成较多的数据计算任务(数据处理)。
透视图可以直观、清晰的展示各类产品在不同月份、不同地区的销售情况及总的销售情况分析,为产品布局提供指导性参考依据(数据可视化)。
另外当小A从数据透视图看到某个月份销售额过高,可以直接点击查看透视表中的数据明细是否存在错误,当小A想查看每个产品销售额的占比,可以新建数据透视表并将透视图切换成饼状图查看。
由此可见,用Excel来做数据分析不仅可以很灵活的满足需求,而且还很实用、很方便,也不会因功能的限制导致对数据无法分析。
通过以上论证,我们发现,既要能够解决问题又要支撑一个长期存在的功能,还要用较低成本以及最快的方式来完成,我们采用最佳的解决方案:“导出”。
既然已经明确最佳解决方案是“导出”,那接下来就从导出的全生命周期看看,如何设计导出功能?
1)明确导出数据限制
无论当前的系统数据量是多少,建议都要做导出条数限制。
因2003版的excel 一个sheet表最多导出65535条数据,2007版的excel是10万4000多条。如果不设限,当用户导出的数据量超过excel单个sheet的数据量时,会出现导出失败的情况,影响用户的正常使用,且产生对系统的不信任情绪。
那应该限制到多少条数据?
我们先来了解一下导出的技术原理,当用户点击导出后,数据会被以excel的形式下载到服务器,服务器再通过网络将文件发送给用户。
在这个过程中,导出条数受限制的原因一个是服务器性能,另一个是用户的电脑性能以及所使用excel版本,在实际产品设计时,根据实际情况,制定一个合理的数据限制即可。
回到开头的场景中,用户不仅要导出数据还要做数据透视表,假设用户使用的03版excel,导出30多个字段,使用大量excel公式,最稳妥的是限制到1万条数据以内。
做了一组极限测试数据供大家参考,使用一台2核4G的服务器、1个用户使用、导出条数是1048576条(导出最大条数)、导出3个字段、使用2010版excel,导出后当使用一个sum公式时,出现了如下图的错误,导致excel异常退出。
2)明确导出格式
数据导出格式有.xls和.csv,.xls是二进制的文件用excel才能打开;.csv是文本文件,用记事本就能打开。而当前用户导出数据后要进行的是数据分析,故只需支持.xls导出。
3)明确导出需求
导出一个excel一个sheet,还是一个excel多个sheet?
考虑到用户导出数据后要对订单数据进行分析,可以与用户明确是否需要按某一维度如客户维度将数据拆分成多个sheet,减少用户操作数据的时间以便能把更多精力放在数据分析。
如果用户不需要按照某一维度拆分数据,则采用导出一个excel一个sheet的方式。
表头是否需要增加序号列?
当用户导出订单数据后,为了让用户准确操作某一行数据,需要有唯一代表一行数据的标记,而在订单导出前是以数据库的主键来标记,对于导出后的订单,则需要自动增加序号列方便用户操作。
是否有内容需要用颜色标注区别?
在导出订单数据中,为了快速掌握销售情况,有些数据是需要特别关注的、而有些不需要。因此,可以使用颜色标注来做区别,让查看人员快速找到自己想要的数据,如可以标注总计快速查看总销售额。
是否需要合并单元格?
对于导出后进行数据分析,不建议使用合并单元格,因excel中合并单元格后仅保留左上角的值如下图所示,这样会使得筛选出现错误,也影响批量的公式使用导致透视表无法分析。
如果导出后只查看数据,可以考虑使用合并单元格。
1)是否需要导出维度
百度释义:维度是事物“有联系”的抽象概念的数量,如时间维度是以时间作为描述、表达变量的度量尺度。
导出维度是指的按照特定场景下,导出以某个字段为主导数据且与该字段相关的其他字段数据。
一般来说,财务和仓库的导出业务场景不同,财务需要以订单维度导出,仓库需要按商品为维度导出,如果无需导出维度,则不需要过度设计。
2)设置表头导出字段
由于订单的一条记录数据字段会很多,包括:订单号、销售类型、客户名称、产品名称、数量、单价、收货人、联系电话、账期、发货时间、预约到仓时间、发货基地名称、发货方式、合同折让率、应收款、已收款、未收款、产季等近30个字段。
而订单分析时,收货人、联系电话、发货时间等字段则无需导出。
因此选择字段导出,可以让客户能够更快速使用并完成分析。导出和查询均要使用筛选,但呈现结果的方式不同,两者的使用场景是可以借鉴的,建议可以放置筛选区。
3)是否需要支持选择行导出
一般来说,导出数据为全量数据,如果用户通过字段选择不出需要的数据,此时要支持用户勾选某些行数据,提示用户当前勾选数据明细及数据条数。
4)其他处理
为了让客户清晰的明白订单是如何导出的,需要在导出时给予导出规则、导出图片、附件形式说明文字提示。
5)根据导出数据量,明确数据处理方式
从技术的角度说,针对较大数据量的导出场景,可以采用异步的处理方式,降低客户的焦急心理。
所谓异步,就是用户点击导出按钮后,后端接收请求并执行读取任务,用户可以不用停留在原处等待,离开当前页面去处理其它工作任务,之后再打开任务页面查看导出结果。
如果有数据可以预先计算,后端可以直接预先计算,同时避免了因采用同步的处理方式导致长时间等待的结果,客户体验会更好,工作效率也会更高。
6)是否需要任务页面
如果用户点击“导出”按钮后,10秒钟内不能完成文件打开,会让用户产生焦虑心理。为了让用户使用体验更佳,需要有一个固定页面即任务页面来承载导出任务列表。
功能,是为解决问题而生,而功能的起源是需求,需求是从场景中找到问题。
由此可见,功能设计流程一定会包含场景选定、问题分析并找准需求、解决方案分析、选定功能、功能设计这5个阶段。
做功能设计时,时刻问自己三个为什么:为什么这是一个问题(问题具备危害性)?为什么要解决这个问题(被解决的价值)?为什么我选定的功能可以解决这个问题(功能的价值)?
导出不仅可以解决文中的“需要更全面的数据分析”的问题,还可以解决“数据离线使用”的问题,导出后,数据以本地文件的形式存在,可离线使用。
另外导出还能解决“数据交付系统外部人员”的问题,导出后,数据以独立文件的形式存在,可以复制,传递。
文章来源:人人都是产品经理 作者:努力的小妖
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
首先看看我们的素材:
当拿到一张原始素材的时候!
怎么办?该怎么入手?怎么找方向?
来吧!
先来看看成稿:
最终设计成果还可以,那么是通过怎样的设计手法达到这样的效果呢?
下面我就来分享一下我的作图思路;
1.需求的梳理和信息收集:
理解核心需求,为设计方向做好前期准备
Slogan:传武(作品名) 副文案:幽府之力,逆转生死
我们漫画类的作品众多,内容风格繁杂,所以拿到需求之后,首先就是要对作品进行“调查”。通过对漫画作品的阅读,理解内容、故事、绘画风格来定性设计的方向。这样才能在设计过程中准确把握住作品调性,才能设计出最贴合作品风格内容的banner,才能把我们作品精髓的内容传递给用户。
比如上面这部作品,SLOGAN“传武”是我们要着重设计展示的。而副标题“幽府之力,逆转生死”也很重要,往往传达出了作品的卖点和调性。
再看看我们拿到的素材,一张单人的简单素材,看起来很单调,似乎没有可切入的地方。这个时候就体现出为什么要先对作品进行“调查”的重要性了!
2. 确定设计方向:
明确设计方向,精准展现作品调性
通过阅读,我们了解到这部作品是一部热血,古风,玄幻作品,还有大致的故事内容,再结合我们的副标题“幽府之力,逆转生死”。
脑子里就已经开始有画面了,“幽府”大概表达了一个场景,而“力量”和“逆转生死”又传达出了一种气势磅礴的场面。这就为我们设计的方向奠定了一个准确的方向。
首先就把我们的素材和文案拉进画框里,进行一个大致的排版找找感觉。第一个左右排版就太常规了,在场面和气势上有些弱。第二个添加了漫画框,想切入一些故事内容一起展示。但又有一些强调漫画框的存在了,磅礴的场景没有展示出来。但注意右边的部分,把主标题排在人物的两边似乎是一个不错的选择!我们就从这里入手。
重新尝试了一下,发现这样布局好像就是我们想要的感觉哈!那既然确定了框架,我们接下来就按照这个方向继续强化出“气势”“力量”的感觉。
我们以人物为中心,想象画面里有力量从人物背后向外“迸发”所以我们的字体可以设计得有一些趋势在里边,也是以一个圆弧为中心向外生长。以光从人物背面照射出来大逆光的视觉,营造一种“力量”迸发的感觉。
3. 颜色的选择:
跟随之前确定的设计方向,提炼选色搭配。
我的方法一般是先从素材本身出发,根据想要达成的视觉风格来延伸出想要的配色。这样得到的颜色更整体,人物素材能更好的融入背景氛围中,也方便后期调整。观察的素材,发现他的颜色都比较灰,缺乏对比,就会显得很“平”,难以营造出我们想要的感觉。所以从人物素材本身的颜色出发,提取同类色和提高饱和度。结合考虑到有利于运营推广的视觉需要“吸睛”。得出了后面一组对比更强烈的颜色。
4. 有主次地进行深入刻画:
画面中最主要的肯定是我们的SLOGAN和人物角色,是我们要重点刻画的对象。剩下的副标题、背景氛围次之。不仅是要在排版上做区分,在视觉感受上也要做出差异化。这样才能有远近虚实的感觉,增加空间感。
我们希望画面具有一定的质感,增加其冲击力。所以我们在刻画背景的时候可以选择一些漫画里比较好的场景,或扉页背景素材来做底图。再叠加上一些纹理材质,再一层一层地来给背景打光,用“叠加”“柔光”“滤色”等图层属性来慢慢提高亮度,最终达到我们想要的效果。
小技巧1:相同光源的照射,传达到不同的物体上时,它的视觉表现时不同的。并不是光源时什么颜色,照射的地方就会是什么颜色。
我们来对比一下两种颜色的实际效果,可以说是很直观了!
小技巧2:为了使素材更完美地融入到背景中,我们可以后期人为地给素材增加一圈高光/轮廓光。这样使画面更融洽的同时,也能让我们的人物变得立体起来!。
再来对比下没加轮廓光的感觉:
真的是少了些味道和细节哈哈,其实在很多时候我们都可以对我们的素材进行二次加工让其提升一定的品质,配合画面以达到更好的视觉效果。
5. SLOGAN的设计:
主文案在我们草图的基础上,结合整体画面的趋势进行细化。(增加毛笔笔触,和优化笔画)。
这里主要分了三层进行处理颜色层(文字层):主要给一个基础颜色;
材质层:因为这两个字的占比比较大,所以可以增加一些纹理细节让画面更丰富耐看;
厚度层:让后面的光源,在我们的字上形成一圈高光,可以突出我们的文字。
增加一些光晕效果,再放上做好的SLOGAN看看效果,好像还不错。
有些同学可能会疑惑这里为什么字体要做一个厚度层,我们也上一下对比图先看下效果:
可以看少了一些些质感和重量,在轻量的风格里ok,但是在我们当前的画面里就差了些感觉,所以才做了厚度层来强调光线照射过来的视觉增强画面冲击力。
之后主要是做一些符合我们画面氛围的漂浮元素,丰富画面。有一定手绘功底的话就再好不过了!
完成,到这一步差不多达成了我们想要实现的效果,“气势”和“力量”的感觉在这么“朴实无华”的素材身上也基本表现到位了。还是比较满意的,差不多可以提审交稿了!
最后在观察观察整体的画面,审视一遍,查缺补漏。
发现我们的画面好像有些燥啊,红色和黄色饱和略微有些高。整体的感觉也不够清晰。最后再做一个调整吧。
降低一些红色和黄色饱和度,在暗部加一些紫色(主文案暗的部分和画面四周的暗部)。增加冷暖对比就好多了。
小技巧3:盖印整个图层——在滤镜里面找到其他——高反差保留,数值根据画面来调。
然后就得到这么一个图层
是不是很神奇?别慌,把这个图层的属性改成线性光看看,画面清晰了很多,也变得更有质感了!
最后看下过程演变图:
总结
1)梳理需求内容:通过阅读漫画作品,深入了解内容并收集整理信息;
2)提炼关键字延展:尝试多种可行方案,最终确定设计方向;
3)slogan的设计:一定要符合画面和作品调性,达到与画面相映成辉的效果;
4)细节把控:完成之后再回过头来审视整体画面,查漏补缺力求做到最好!
文章来源:UI中国 作者:腾讯动漫TCD
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
构建库的常见方法有两种:一种是自己手动构建webpack库打包,设置output为 library; 另一种是基于vue-cli3输出库资源包。我们采用第二种vue脚手架的方式构建库。
新增编译库命令
// package.json
"scripts": {
// ...
"lib": "vue-cli-service build --target lib --name Step --dest dist packages/index.js"
}
// packages/index.js 默认打包Step
import Step from '../steps/src/step';
Step.install = function(Vue) {
Vue.component(Step.name, Step);
};
export default Step;
--name: 库名称。
--target: 构建目标,默认为应用模式。这里修改为 lib 启用库模式。
--dest: 输出目录,默认 dist。
[entry]: 最后一个参数为入口文件,默认为 src/App.vue。
更多详细配置查看 ☛ vue脚手架官网
如果该库依赖于其他库,请在vue.config.js 配置 externals
// vue.config.js
module.exports = {
configureWebpack:{
externals: {
vue: 'Vue',
'vue-router':'VueRouter',
axios: 'axios'
}
}
}
执行 npm run lib 就可以发现我们的库被打包到了 根目录的dist文件夹下。
添加 .npmignore 文件(可选)
和 .gitignore 的语法一样,具体需要提交什么文件,看各自的实际情况
# 忽略目录
examples/
packages/
public/
# 忽略指定文件
vue.config.js
babel.config.js
*.map
配置npm库信息
配置package.json文件,以发布库文件。
{
"name": "gis",
"version": "1.2.5",
"description": "基于 Vue 的库文件",
"main": "dist/gis.umd.min.js",
"keyword": "vue gis",
"private": false,
"files": ["dist"],
"license": "MIT"
}
name: 包名,该名字是唯一的。可在 npm 官网搜索名字,如果存在则需换个名字。
version: 版本号,每次发布至 npm 需要修改版本号,不能和历史版本号相同。
description: 描述。
main: 入口文件,该字段需指向我们最终编译后的包文件。
keyword:关键字,以空格分离希望用户最终搜索的词。
author:作者
files: 要上传的文件
private:是否私有,需要修改为 false 才能发布到 npm
license: 开源协议
dependencies: 依赖库
注意每次发布新的库,需要更改版本号,规则如下:
"version": "1.2.5" 主版本号为 1,次版本号 2,修订号 5
主版本号(Major):当你做了不兼容的API修改
次版本号(Minor):当你做了向下兼容的功能性新增
修订号(Patch):当你做了向下兼容的问题修正
登录npm
首先设置登录的npm镜像地址
npm config set registry http://168.20.20.57.4873
然后在终端执行登录命令,输入用户名、密码、邮箱即可登录
npm login
接着发布库资源到npm
npm publish
最后发布成功可到官网查看对应的包并下载
npm install package_name
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务目录
定义函数的方式有三种:
new Function
(一般不用)
-
// 函数的声明
-
function fn() {
-
console.log("我是JS中的一等公民-函数!!!哈哈");
-
}
-
fn();
函数表达式就是将一个匿名函数赋值给一个变量。函数表达式必须先声明,再调用。
-
// 函数表达式
-
var fn = function() {
-
console.log("我是JS中的一等公民-函数!!!哈哈");
-
};
-
fn();
下面是一个根据条件定义函数的例子:
-
if (true) {
-
function f () {
-
console.log(1)
-
}
-
} else {
-
function f () {
-
console.log(2)
-
}
-
}
以上代码执行结果在不同浏览器中结果不一致。我们可以使用函数表达式解决上面的问题:
-
var f
-
-
if (true) {
-
f = function () {
-
console.log(1)
-
}
-
} else {
-
f = function () {
-
console.log(2)
-
}
-
}
函数声明如果放在if-else的语句中,在IE8的浏览器中会出现问题,所以为了更好的兼容性我们以后最好用函数表达式,不用函数声明的方式。
在前面的学习中我们了解到函数也是对象。注意:函数是对象,对象不一定是函数,对象中有__proto__原型,函数中有prototype原型,如果一个东西里面有prototype,又有__proto__,说明它是函数,也是对象。
-
function F1() {}
-
-
console.dir(F1); // F1里面有prototype,又有__proto__,说明是函数,也是对象
-
-
console.dir(Math); // Math中有__proto__,但是没有prorotype,说明Math不是函数
对象都是由构造函数创建出来的,函数既然是对象,创建它的构造函数又是什么呢?事实上所有的函数实际上都是由Function构造函数创建出来的实例对象。
所以我们可以使用Function构造函数创建函数。
语法:new Function(arg1,arg2,arg3..,body);
arg是任意参数,字符串类型的。body是函数体。
-
// 所有的函数实际上都是Function的构造函数创建出来的实例对象
-
var f1 = new Function("num1", "num2", "return num1+num2");
-
console.log(f1(10, 20));
-
console.log(f1.__proto__ == Function.prototype);
-
-
// 所以,函数实际上也是对象
-
console.dir(f1);
-
console.dir(Function);
-
// 普通函数
-
function f1() {
-
console.log("我是普通函数");
-
}
-
f1();
-
-
// 构造函数---通过new 来调用,创建对象
-
function F1() {
-
console.log("我是构造函数");
-
}
-
var f = new F1();
-
-
// 对象的方法
-
function Person() {
-
this.play = function() {
-
console.log("我是对象中的方法");
-
};
-
}
-
var per = new Person();
-
per.play();
this
的指向
函数的调用方式决定了 this
指向的不同:
调用方式 | 非严格模式 | 备注 |
---|---|---|
普通函数调用 | window | 严格模式下是 undefined |
构造函数调用 | 实例对象 | 原型方法中 this 也是实例对象 |
对象方法调用 | 该方法所属对象 | 紧挨着的对象 |
事件绑定方法 | 绑定事件对象 | |
定时器函数 | window |
-
// 普通函数
-
function f1() {
-
console.log(this); // window
-
}
-
f1();
-
-
// 构造函数
-
function Person() {
-
console.log(this); // Person
-
// 对象的方法
-
this.sayHi = function() {
-
console.log(this); // Person
-
};
-
}
-
// 原型中的方法
-
Person.prototype.eat = function() {
-
console.log(this); // Person
-
};
-
var per = new Person();
-
console.log(per); // Person
-
per.sayHi();
-
per.eat();
-
-
// 定时器中的this
-
setInterval(function() {
-
console.log(this); // window
-
}, 1000);
了解了函数 this 的指向之后,我们知道在一些情况下我们为了使用某种特定环境的 this 引用,需要采用一些特殊手段来处理,例如我们经常在定时器外部备份 this 引用,然后在定时器函数内部使用外部 this 的引用。
然而实际上 JavaScript 内部已经专门为我们提供了一些函数方法,用来帮我们更优雅的处理函数内部 this 指向问题。这就是接下来我们要学习的 call、apply、bind 三个函数方法。call()、apply()、bind()这三个方法都是是用来改变this的指向的。
call()
方法调用一个函数, 其具有一个指定的 this
值和分别地提供的参数(参数的列表)。
apply()
方法调用一个函数, 其具有一个指定的 this
值,以及作为一个数组(或类似数组的对象)提供的参数。
注意:call()
和 apply()
方法类似,只有一个区别,就是 call()
方法接受的是若干个参数的列表,而 apply()
方法接受的是一个包含多个参数的数组。
call语法:
fun.call(thisArg[, arg1[, arg2[, ...]]])
call参数:
thisArg
arg1, arg2, ...
apply语法:
fun.apply(thisArg, [argsArray])
apply参数:
thisArg
argsArray
apply()
与 call()
相似,不同之处在于提供参数的方式。
apply()
使用参数数组而不是一组参数列表。例如:
fun.apply(this, ['eat', 'bananas'])
-
function f1(x, y) {
-
console.log("结果是:" + (x + y) + this);
-
return "666";
-
}
-
f1(10, 20); // 函数的调用
-
-
console.log("========");
-
-
// apply和call方法也是函数的调用的方式
-
// 此时的f1实际上是当成对象来使用的,对象可以调用方法
-
// apply和call方法中如果没有传入参数,或者是传入的是null,那么调用该方法的函数对象中的this就是默认的window
-
f1.apply(null, [10, 20]);
-
f1.call(null, 10, 20);
-
-
// apply和call都可以让函数或者方法来调用,传入参数和函数自己调用的写法不一样,但是效果是一样的
-
var result1 = f1.apply(null, [10, 20]);
-
var result2 = f1.call(null, 10, 20);
-
console.log(result1);
-
console.log(result2);
-
// 通过apply和call改变this的指向
-
function Person(name, sex) {
-
this.name = name;
-
this.sex = sex;
-
}
-
//通过原型添加方法
-
Person.prototype.sayHi = function(x, y) {
-
console.log("您好啊:" + this.name);
-
return x + y;
-
};
-
var per = new Person("小三", "男");
-
var r1 = per.sayHi(10, 20);
-
-
console.log("==============");
-
-
function Student(name, age) {
-
this.name = name;
-
this.age = age;
-
}
-
var stu = new Student("小舞", 18);
-
var r2 = per.sayHi.apply(stu, [10, 20]);
-
var r3 = per.sayHi.call(stu, 10, 20);
-
-
console.log(r1);
-
console.log(r2);
-
console.log(r3);
apply和call都可以改变this的指向。调用函数的时候,改变this的指向:
-
// 函数的调用,改变this的指向
-
function f1(x, y) {
-
console.log((x + y) + ":===>" + this);
-
return "函数的返回值";
-
}
-
//apply和call调用
-
var r1 = f1.apply(null, [1, 2]); // 此时f1中的this是window
-
console.log(r1);
-
var r2 = f1.call(null, 1, 2); // 此时f1中的this是window
-
console.log(r2);
-
console.log("=============>");
-
//改变this的指向
-
var obj = {
-
sex: "男"
-
};
-
// 本来f1函数是window对象的,但是传入obj之后,f1的this此时就是obj对象
-
var r3 = f1.apply(obj, [1, 2]); //此时f1中的this是obj
-
console.log(r3);
-
var r4 = f1.call(obj, 1, 2); //此时f1中的this是obj
-
console.log(r4);
调用方法的时候,改变this的指向:
-
//方法改变this的指向
-
function Person(age) {
-
this.age = age;
-
}
-
Person.prototype.sayHi = function(x, y) {
-
console.log((x + y) + ":====>" + this.age); //当前实例对象
-
};
-
-
function Student(age) {
-
this.age = age;
-
}
-
var per = new Person(10); // Person实例对象
-
var stu = new Student(100); // Student实例对象
-
// sayHi方法是per实例对象的
-
per.sayHi(10, 20);
-
per.sayHi.apply(stu, [10, 20]);
-
per.sayHi.call(stu, 10, 20);
总结
apply的使用语法:
1 函数名字.apply(对象,[参数1,参数2,...]);
2 方法名字.apply(对象,[参数1,参数2,...]);
call的使用语法
1 函数名字.call(对象,参数1,参数2,...);
2 方法名字.call(对象,参数1,参数2,...);
它们的作用都是改变this的指向,不同的地方是参数传递的方式不一样。
如果想使用别的对象的方法,并且希望这个方法是当前对象的,就可以使用apply或者是call方法改变this的指向。
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也可以接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
bind方法是复制的意思,本质是复制一个新函数,参数可以在复制的时候传进去,也可以在复制之后调用的时候传入进去。apply和call是调用的时候改变this指向,bind方法,是复制一份的时候,改变了this的指向。
语法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数:
thisArg
arg1, arg2, ...
返回值:
返回由指定的this值和初始化参数改造的原函数的拷贝。
示例1:
-
function Person(name) {
-
this.name = name;
-
}
-
Person.prototype.play = function() {
-
console.log(this + "====>" + this.name);
-
};
-
-
function Student(name) {
-
this.name = name;
-
}
-
var per = new Person("人");
-
var stu = new Student("学生");
-
per.play();
-
// 复制了一个新的play方法
-
var ff = per.play.bind(stu);
-
ff();
示例2:
-
//通过对象,调用方法,产生随机数
-
function ShowRandom() {
-
//1-10的随机数
-
this.number = parseInt(Math.random() * 10 + 1);
-
}
-
//添加原型方法
-
ShowRandom.prototype.show = function() {
-
//改变了定时器中的this的指向了
-
window.setTimeout(function() {
-
//本来应该是window, 现在是实例对象了
-
//显示随机数
-
console.log(this.number);
-
}.bind(this), 1000);
-
};
-
//实例对象
-
var sr = new ShowRandom();
-
//调用方法,输出随机数字
-
sr.show();
call 和 apply 特性一样
this
的指向
null
或者 undefined
则内部 this 指向 window
bind
-
function fn(x, y, z) {
-
console.log(fn.length) // => 形参的个数
-
console.log(arguments) // 伪数组实参参数集合
-
console.log(arguments.callee === fn) // 函数本身
-
console.log(fn.caller) // 函数的调用者
-
console.log(fn.name) // => 函数的名字
-
}
-
-
function f() {
-
fn(10, 20, 30)
-
}
-
-
f()
函数可以作为参数,也可以作为返回值。
函数是可以作为参数使用,函数作为参数的时候,如果是命名函数,那么只传入命名函数的名字,没有括号。
-
function f1(fn) {
-
console.log("我是函数f1");
-
fn(); // fn是一个函数
-
}
-
-
//传入匿名函数
-
f1(function() {
-
console.log("我是匿名函数");
-
});
-
// 传入命名函数
-
function f2() {
-
console.log("我是函数f2");
-
}
-
f1(f2);
作为参数排序案例:
-
var arr = [1, 100, 20, 200, 40, 50, 120, 10];
-
//排序---函数作为参数使用,匿名函数作为sort方法的参数使用,此时的匿名函数中有两个参数,
-
arr.sort(function(obj1, obj2) {
-
if (obj1 > obj2) {
-
return -1;
-
} else if (obj1 == obj2) {
-
return 0;
-
} else {
-
return 1;
-
}
-
});
-
console.log(arr);
-
function f1() {
-
console.log("函数f1");
-
return function() {
-
console.log("我是函数,此时作为返回值使用");
-
}
-
-
}
-
-
var ff = f1();
-
ff();
作为返回值排序案例:
-
// 排序,每个文件都有名字,大小,时间,可以按照某个属性的值进行排序
-
// 三个文件,文件有名字,大小,创建时间
-
function File(name, size, time) {
-
this.name = name; // 名字
-
this.size = size; // 大小
-
this.time = time; // 创建时间
-
}
-
var f1 = new File("jack.avi", "400M", "1999-12-12");
-
var f2 = new File("rose.avi", "600M", "2020-12-12");
-
var f3 = new File("albert.avi", "800M", "2010-12-12");
-
var arr = [f1, f2, f3];
-
-
function fn(attr) {
-
// 函数作为返回值
-
return function getSort(obj1, obj2) {
-
if (obj1[attr] > obj2[attr]) {
-
return 1;
-
} else if (obj1[attr] == obj2[attr]) {
-
return 0;
-
} else {
-
return -1;
-
}
-
}
-
}
-
console.log("按照名字排序:**********");
-
// 按照名字排序
-
var ff = fn("name");
-
// 函数作为参数
-
arr.sort(ff);
-
for (var i = 0; i < arr.length; i++) {
-
console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
-
}
-
-
console.log("按照大小排序:**********");
-
// 按照大小排序
-
var ff = fn("size");
-
// 函数作为参数
-
arr.sort(ff);
-
for (var i = 0; i < arr.length; i++) {
-
console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
-
}
-
-
console.log("按照创建时间排序:**********");
-
// 按照创建时间排序
-
var ff = fn("time");
-
// 函数作为参数
-
arr.sort(ff);
-
for (var i = 0; i < arr.length; i++) {
-
console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
-
}
-
无缝轮播一直是面试的热门题目,而大部分答案都是复制第一张到最后。诚然,这种方法是非常标准,那么有没有另类一点的方法呢?
第一种方法是需要把所有图片一张张摆好,然后慢慢移动的,
但是我能不能直接不摆就硬移动呢?
如果你使用过vue的transition
,我们是可以通过给每一张图片来添加入场动画和离场动画来模拟这个移动
这样看起来的效果就是图片从右边一直往左移动,但是这个不一样的地方是,我们每一个元素都有这个进场动画和离场动画,我们根本不用关心它是第几个元素,你只管轮播就是。
很简单,我们自己实现一个transtition
的效果就好啦,主要做的是以下两点
xx-enter-active
动画
xx-leave-active
, 注意要让动画播完才消失
function hide(el){
el.className = el.className.replace(' slide-enter-active','')
el.className += ' slide-leave-active' el.addEventListener('animationend',animationEvent)
} function animationEvent(e){
e.target.className = e.target.className.replace(' slide-leave-active','')
e.target.style.display = 'none' e.target.removeEventListener('animationend',animationEvent)
} function show(el){
el.style.display = 'flex' el.className += ' slide-enter-active' }
这里我们使用了animationend
来监听动画结束,注意这里每次从新添加类的时候需要重新添加监听器,不然会无法监听。如果不使用这个方法你可以使用定时器的方式来移除leave-active类。
function hide(el){
el.className = el.className.replace(' slide-enter-active','')
el.className += ' slide-leave-active' setTimeout(()=>
{ //动画结束后清除class el.className = el.className.replace(' slide-leave-active','')
el.style.display = 'none' }, ANIMATION_TIME) //这个ANIMATION_TIME为你在css中动画执行的时间 }
.slide-enter-active{ position: absolute; animation: slideIn ease .5s forwards;
} .slide-leave-active{ position: absolute; animation: slideOut ease .5s forwards;
} @keyframes slideIn {
0%{ transform: translateX(100%);
}
100%{ transform: translateX(0);
}
} @keyframes slideOut {
0%{ transform: translateX(0);
}
100%{ transform: translateX(-100%);
}
}
需要注意的是这里的 forwards
属性,这个属性表示你的元素状态将保持动画后的状态,如果不设置的话,动画跑完一遍,你的元素本来执行了离开动画,执行完以后会回来中央位置杵着。这个时候你会问了,上面的代码不是写了,动画执行完就隐藏元素吗?
如果你使用上面的setTimeout来命令元素执行完动画后消失,那么可能会有一瞬间的闪烁,因为实际业务中,你的代码可能比较复杂,setTimeout没法在那么精准的时间内执行。保险起见,就让元素保持动画离开的最后状态,即translateX(-100%)
。此时元素已经在屏幕外了,不用关心它的表现了
很简单,我们进一个新元素的时候同时移除旧元素即可,两者同时执行进场和离场动画即可。
function autoPlay(){
setTimeout(()=>{
toggleShow(新元素, 旧元素) this.autoPlay()
},DURATION) //DURATION为动画间隔时间 } function toggleShow(newE,oldE){ //旧ele和新ele同时动画 hide(oldE)
show(newE)
}
手机UI中的交互是保持产品鲜活生命力的源动力。好的交互可以帮助用户快速地获得反馈,认知布局,增强体验感和沉浸感。
手机UI中的交互是保持产品鲜活生命力的源动力。好的交互可以帮助用户快速地获得反馈,认知布局,增强体验感和沉浸感。这里为大家整理了一些优秀并富有创意的交互作品,为你的产品设计注入灵感。
--手机appUI设计--
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
更多精彩文章:
写在前面
在平时的设计过程当中,你可能会有这样的疑惑,为什么在大部分APP中,当单个按钮和多个按钮同时存在时,最重要的按钮一般都会放置在页面的右侧呢?如果最重要的按钮放在左侧又有什么问题呢?按钮放在右侧的原因是什么呢?它又有什么理论依据呢?接下来带着这些疑问,开始我们今天所要介绍的内容:交互心理学之古腾堡原则
古腾堡原则的起源
古腾堡原则是由14世纪西方活字印刷术的发明人约翰·古腾堡提出,早在20世纪50年代,他在设计报纸的过程中,提出了一项原则,认为人的阅读方式应该是遵循某种习惯进行的,就像读书一样,由左到右,从上到下。这其中蕴含着什么信息呢?经过研究最终得出被后人所熟知的结论:古腾堡原则,并附上了一张图,名为「古腾堡图」。古腾堡图将画面所呈现的内容分成四个象限:
1、第一视觉区(POA):左上方,用户首先注意到的地方
2、强休息区(SFA):右上方,较少被注意到
3、弱休息区(WFA):左下方,最少被注意到
4、终端视觉区(TA):右下方,视觉流终点
从图中可以看出,用户视线很自然的会从第一视觉区开始,逐渐移动到终端休息区。整个阅读过程视线都会沿着一条方向轴开始从左到右浏览。用户会更容易关注到页面的开始与结束区域,而中间的段落则很少被关注到。古腾堡揭示了一个实用的视觉轨迹规律:阅读引力是从上到下,从左到右。
遵循古腾堡原则把关键信息放在左上角、中间和右下角,能够更好的体现元素的重要性。例如:我们平时所看到的页面弹窗、各种证明文件和合同文件等等。
古腾堡图通过对设计元素的重量与元素布局和组成方式进行调和,指导眼睛的运动轨迹。让用户迅速获取有价值的信息,同时用户对信息的熟悉程度也是影响眼睛运动轨迹的因素之一。
而随着互联网的兴起,古腾堡原则也逐渐被应用到APP设计和网页设计当中。接下来让我们来看看他在界面中的实际应用。
在设计中的应用
1.1 底部单个按钮
这种形式在引导用户操作的页面中最为常见,为了能够保证用户对内容进行阅读,所以将按钮摆放在页面底部,内容放在顶部,这样的摆放即符合用户由上到下的阅读习惯又达到了产品预期的目标。
1.2 底部垂直双按钮
上面我们提到了单个按钮的摆放思路,接下来看一下垂直双按钮的摆放思路是怎么样的。如果一个界面上同时存在两个优先级不同的按钮,并且产品希望用户对每一个按钮都有足够的关注度,那么垂直摆放是最佳选择,虽然垂直双按钮在样式上做了区分,但用户同样会停留一段时间将按钮的内容进行对比思考。
那么,按照古腾堡原则,重要的按钮应该放在页面最底部,原则上它应该是这样的:
仔细观察上图,有没有发现浅色按钮很容易被忽略掉,这样就违背了产品要保证每一个按钮都要有足够关注度的初衷,所以我们要违背古腾堡原则来满足业务需求,正如我们所看到的微信授权页面一样,
为了保证「同意」与「拒绝」这两个独立的按钮能够被用户足够的重视,并且其中的任意一个按钮不会被轻易的忽略掉,这里将「同意」按钮颜色加重,并且放在「拒绝」按钮之上,让眼睛原本垂直向下的运动轨迹产生回流的变化。
小结
原则是设计的基础,并非一成不变,要合理权衡设计原则与产品目标之间的关系。
2、顶部按钮分析
由于顶部导航栏空间有限,导致按钮相对较小,并且不便于点击操作,所以这类顶部按钮适用于修改内容的编辑页面,即可以避免误触,又可以让用户关注内容本身。关键按钮至于顶部,还可以缩短用户眼睛的运动路径,让用户更容易注意到其状态的变化状态。
小结
顶部按钮更关注可编辑的内容区域,并非按钮。而底部按钮则更关注按钮本身。并非内容。
3、水平按钮分析
除了上面提到的顶部按钮和底部按钮,还有水平摆放的按钮,比如淘宝详情页、京东详情页、网易严选详情页的「加入购物车」和「立即购买」按钮,界面中的「立即购买」按钮都放在了右下角,结合古腾堡原则的视觉终点说明,右下角为视觉终端区域,即视觉最终停留的位置,所以他们都将与转化率密切相关的「立即购买」按钮放在了界面的右下角,让用户更容易关注到。
再比如比较常见的「取消」和「确认」弹窗样式,通常是在需要让用户确认某种操作行为时出现,有可能是提交表单、协议授权、获取用户信息等等,为了防止用户误操作,这也是提升产品体验的小细节。
平常我们所看到的弹窗,推荐按钮都是在右侧,那么将推荐按钮放在左侧会怎么样?如下图所示:
不难看出推荐按钮放在右侧后,视觉在水平方向轴上产生了回流。
弹窗的目的是想让用户点击「确认」按钮,如果将「确认」放在左侧,根据古腾堡原则,用户的视线会不由自主的向右侧移动,也就是「取消」按钮的位置,想要回到左侧「确认」按钮位置就需要移动视线,并且眼睛的运动轨迹会在水平方向轴上来回的往复运动,无形中增加了用户选择时长。如果将「确认」放在右侧,「取消」放在左侧则可以为用户提高操作效率。
在实际产品中的应用案例:
小结
当产品想要让用户进行某种操作时,主要按钮放在右边
总结
1、古腾堡图第一视觉区,强休息区,弱休息区,终端视觉区
2、原则是设计的基础,并非一成不变,要合理权衡设计原则与产品目标之间的关系
3、顶部按钮更关注可编辑的内容区域,并非按钮。而底部按钮则更关注按钮本身。并非内容
4、当产品想要让用户进行某种操作时,主要按钮放在右边
文章来源:UI中国 作者:Coldrain1
蓝蓝设计( www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 、平面设计服务
蓝蓝设计的小编 http://www.lanlanwork.com