最近小米的新logo可谓是引起了新一轮的吐槽和狂欢,这个价值两百万设计的logo来头不小,又是生命感又是超椭圆,虽然说是说知道“敲一榔头不值钱,知道往哪里敲最难得”,但是这个类似桌面主题图标的效果还是让一些网友吐槽“我上我也行”,那么,在四月一日这个可以随便做梦的日子里,就来试试怎么赚上几个200万吧
本文内容相当基础,所以只是图一乐用的
什么?想知道两百万生成器怎么做?直接跳转底部,我怎么会骗你呢
中间涉及的代码放在了这里:CodePen
摸鱼流
不忘初心,重回切图仔时代,管他什么兼容性浏览器差异,img就一个字,我只打一次
CSS流
使用圆角
生命之理,方圆之间,所以使用border-radius贯彻这种哲理是最佳之选,根据V站老哥的发现,新logo刚刚发布的时候,官网就是单纯在旧的基础上加了个圆角,这体现了张弛有度的人生哲学,妙哉妙哉

使用蒙版
根据设计师所说,新的图标的超椭圆来自于|X|^n+|y|^n=1这个公式,看来直接画出来的确有些难度,那么就在设计图的基础上使用蒙板解决吧
这次要使用的蒙板长这样:
mask是什么可以参见MDN,如果想要了解如何更好地使用,可以阅读奇妙的 CSS MASK
mask是一个CSS属性,可以使用图片或者svg来对图片进行裁剪,也就是说,如果想要让图片可以裁剪出这样的边缘,可以使用
mask-image来导入蒙板,然后设置mask-size使其贴合即可
#img-mask{
position: relative;
width: 100px;
height: 100px;
background-image: url("https://i.loli.net/2021/04/01/E8Rk27LPWNIbwBH.png");
background-size:100% 100%;
background-position: center;
-webkit-mask-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAxIiBoZWlnaHQ9IjMwMSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTUwLjUyNyAzMDAuMjY4Yy0yMy44MDktLjMtNDcuNTg4LS40OC03MS4wOTctNS40ODdhOTIuMjcgOTIuMjcgMCAwMS03My4wNzYtNzQuMjc2IDM3OS4xMTYgMzc5LjExNiAwIDAxLjA2LTEzOC41MzZjNy43NjYtNDAuOTAxIDMzLjcwNC02NS45NyA3NC4xNTUtNzQuOTY2QzExNi40OTMtLjg4MyAxNTIuOTI2LjU4NiAxODkuNDIgMi41NjVjMjAuNzUxIDEuMTQgNDAuODcxIDUuMDY4IDU5LjQ2MyAxNS4yMzMgMjQuNDk4IDEzLjQ5NCAzOC42NTIgMzQuNzU0IDQ0Ljc5OSA2MS41MDIgOC4wNjYgMzUuMjA0IDYuNjU3IDcxLjAwNyA1LjE4OCAxMDYuNzIxYTE2NC45NDIgMTY0Ljk0MiAwIDAxLTguNjk2IDQ4LjU0NyA4OC4wNCA4OC4wNCAwIDAxLTU1LjQ0NSA1NS45MjVjLTIxLjYyIDcuOTQ2LTQ0LjE2OSA4Ljk5NS02Ni44MzkgOS43NzUtNS43ODcuMTUtMTEuNTc0IDAtMTcuMzYyIDB6IiBmaWxsPSIjMDAwIi8+PC9zdmc+");
-webkit-mask-size:100% 100%;
}
使用伪元素
为了节省大家的流量,我们还可以使用省流大师为大家提供高清又节省流量的体验,接下来试一下单纯使用CSS绘制中间的MI字吧,代码来自stevenlei
首先,来实现字母M的轮廓,之后再解决两条I。
因为其类似矩形,因此可以使用border来实现。使用::before来在元素上面绘制,通过设置四条边的大小绘制拱门,然后针对右上角的弧度,使用border-top-right-radius单独设置,当然,左上角的直角也同理。
#div-box-shadow::before {
content: '';
display: block;
position: absolute;
width: 45px;
height: 41px;
top: 30px;
left: 18px;
box-sizing: border-box;
border-color: #fff;
border-style: solid;
border-left-width: 10px;
border-right-width: 10px;
border-top-width: 8px;
border-radius: 2px;
border-top-right-radius: 10px;
border-top-left-radius: 0px;
border-bottom-width: 0;
}
接下来,实现两条直线。
不过这两条直线长度不一,如何使用一个伪元素来表示?(毕竟::before已经用来画n了)我们把目光转向box-shadow
box-shadow介绍参见MDN,基本语法如下
/* x偏移量 | y偏移量 | 阴影颜色 */
box-shadow: 60px -16px teal;
/* x偏移量 | y偏移量 | 阴影模糊半径 | 阴影颜色 */
box-shadow: 10px 5px 5px black;
/* x偏移量 | y偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色 */
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
/* 插页(阴影向内) | x偏移量 | y偏移量 | 阴影颜色 */
box-shadow: inset 5em 1em gold;
/* 任意数量的阴影,以逗号分隔 */
box-shadow: 3px 3px red, -1em 0 0.4em olive;
/* 全局关键字 */
box-shadow: inherit;
box-shadow: initial;
box-shadow: unset;
因此,我们设置宽高画出一个伪小方块后,就可以通过绘制多个无模糊的阴影(影分身),再设置偏移拼接出两个长方形
#div-box-shadow::after {
content: '';
display: block;
position: absolute;
width: 12px;
height: 17px;
top: 47px;
left: 35px;
background-color: #fff;
border-radius: 2px;
box-shadow: 0px 7px 0px 0px #fff,
35px -17px 0px 0px #fff,
35px -4px 0px 0px #fff,
35px 8px 0px 0px #fff;
}
合起来的效果如下
SVG流
其实图片无论是在传输还是在显示上都是不太合适,对于这种简单的logo,让我们来试试使用svg进行绘制
svg的介绍参见MDN,想了解如何绘制svg元素中的path,可以阅读MDN-Paths。本质上就是不断地移动画笔进行连接,得到一个几何轮廓。对于外面的“超椭圆”,想到了两种方式进行实现。
首先,既然有了生成公式|X|^3+|y|^3=1,那么可以先计算出多个点进行拟合,然后依次连接,不过因为还是不够优雅,我们使用贝塞尔曲线来绘制,如果感兴趣的话,强烈推荐阅读一下这个介绍,下面摘抄一些关键部分
- 控制点不总是在曲线上
- 曲线的阶次等于控制点的数量减一。 对于两个点我们能得到一条线性曲线(直线),三个点 — 一条二阶曲线,四个点 — 一条三阶曲线。
- 曲线总是在控制点的凸包内部
如何从控制点得到曲线?过程如下
绘制控制点。在上面的演示中,它们标有:1、2 和 3。
创建控制点 1 → 2 → 3 间的线段. 在上面的演示中它们是棕色的。
参数 t 从 0 to 1 变化。 在上面的演示中取值 0.05:循环遍历 0, 0.05, 0.1, 0.15, ... 0.95, 1。
对于每一个 t 的取值:在每一个棕色线段上我们取一个点,这个点距起点的距离按比例 t 取值。由于有两条线段,我们能得到两个点。
例如,当 t=0 — 所有点都在线段起点处,当 t=0.25 — 点到起点的距离为线段长度的 25%,当 t=0.5 — 50%(中间),当 t=1 — 线段终点。 连接这些点,下面这张图中连好的线被绘制成蓝色。
现在在蓝色线段上取一个点,距离比例取相同数值的 t。也就是说,当 t=0.25(左图)时,我们取到的点位于线段的左 1/4 终点处,当 t=0.5(右图)时 — 线段中间。在上图中这一点是红色的。
随着 t 从 0 to 1 变化,每一个 t 的值都会添加一个点到曲线上。这些点的集合就形成的贝塞尔曲线。它在上面的图中是红色的,并且是抛物线状的。
回到前端,如果要绘制简单圆弧,我们要创建path元素,然后使用二次贝塞尔曲线指令q来绘制,前两项是相对于前一个点的位置差,因此绘制第一个q之前,需要移动到一个起始点。
Q x1 y1, x y
(or)
q dx1 dy1, dx dy
代码如下,来自:hyrious
<svg viewBox="0 0 100 100" style="width: 100px; height: 100px">
<path fill="#ff6700" d="
M 50,10
q 40,0 40,40
q 0,40 -40,40
q -40,0 -40,-40
q 0,-40 40,-40
" />
<path fill="transparent" stroke="white" stroke-width="6" d="
M 31,36
v 30
M 31,39
h 17
a 8,8,90,0,1,8,8
v 19
M 43.5,48
v 18
M 68,36
v 30
" />
</svg>
其中,M 50,10就是移动到50,10处,q 40,0 40,40就是在前一个点的基础上在x相差40px得到的点作为控制点,而40,40作为这个曲线的终点。因此可以绘制出一个50,10->40,40,控制点为90,10的弧线,以此类推,至于其他绘制直线的指令就用到的时候再查吧
效果如下(还是相当简洁又好康的):
CANVAS流
这个还是非常粗暴的,绘图和导出在此按下不表,如何注入灵魂,可以使用 ctx.globalCompositeOperation
来设置图片叠加绘制的逻辑,例如使用
ctx.globalCompositeOperation = "destination-in";
就可以在旧图层上只保留新图层中不为透明像素对应位置的像素,类似于遮罩,除此之外还有其他各种选项,可以实现多钟混合效果。
代码如下:
let c,ctx;
function handlePicUpload(e){
const file = e.files[0];//当上传了图片之后
url = URL.createObjectURL(file);
console.log(url)//blob:
let img = new Image();
c=document.getElementById("canvas-logo");//这是一个canvas
ctx=c.getContext("2d");
img.onload = function(){
ctx.drawImage(img,0,0,100,100);//先绘制原来的图像
ctx.globalCompositeOperation = "destination-in";//修改为遮罩模式
let mask = document.getElementById("mask");//获得价值两百万的遮罩
ctx.drawImage(mask,0,0,100,100);
let exportBtn = document.getElementById("export-btn");
exportBtn.disabled = false;
}
img.src=url;
e.value='';
}
function handlePicExport(){//将画布内容导出
console.log(c)
c.toBlob(function(blob) {
console.log(blob)
let link = document.createElement('a');
link.download = 'two_million.png';
link.href = URL.createObjectURL(blob);
link.click();
URL.revokeObjectURL(link.href);//销毁blob,释放内存
}, 'image/png');
}
大功告成!现在可以上传自己的头像,然后让它瞬间身价百万,这不就是大家都喜欢的两百万生成器吗(不是)
不过话说回来如果蹭热度做一个小程序,就是给头像一键上个200w的Buff,说不定会意外地流行呢...