当前位置: 首页 > ai >正文

【总结】1111- 如何搞定Banner背景自动换色的功能?

一、背景概述

我是谁?我是一名前端攻城狮,这周刚到公司不久,我司的产品经理就跑到我面前说:“浪哥,昨晚我看到某 APP 首页 Banner 切换时,Banner 区域的背景色会跟随 Banner 图片的色调一起切换,这个功能我们能实现么?”。听完他的话,我在脑海中脑补了一下他描述的功能,然后淡淡的回了一句 —— 应该可以吧。话音刚落,他立马来了一句 —— ”那就这么定了,我们在下个版本就上线这个功能“。

fc33c8f6fd0527e58243b967928e6a96.png

竟然还有这么玩的,看来下次不能随随便便使用 应该” 二字了。既然是自己挖的坑,那还得自己填,所以我重新梳理了一下功能需求。之后,发现其实这个功能最核心的难点就是提取图片的主题色。那么接下来,我们的重心就是寻找如何提取图片主题色的方案。

二、提取主题色方案探索

这当然难不倒一个熟练掌握百度、谷歌、Bing 等主流搜索引擎的程序猿,经过一番信息检索与筛选。在 如何对前端图片主题色进行提取?这篇文章详细告诉你 这篇文章中,我找到了比较常用的主题色提取算法,主要包括 最小差值法、中位切分法、八叉树算法、聚类、色彩建模法 等(可以考虑放几张算法概述图?)。第一眼看到这么多陌生的算法后,我的第一感受是这样的:

4885adec8e7241c0b27a7bf05b6428d2.png

我只是想提取图片的主题色啊,能不能来点简单粗暴的方法。经过一番搜索,我找到了一种纯 CSS 实现的方案。在 小技巧!CSS 提取图片主题色功能探索 这篇文章中,介绍了通过 filter: blur()transform: scale() 来获取图片的主题色。具体的实现效果如下图所示:

d6fa302fe5d6e7b7438a36ff91deddd2.png

在线示例:https://codepen.io/Chokcoco/pen/poRBQGg

这种方案实现起来并不会复杂,但会存在以下的问题:

  • 只能大致拿到图片的主题色,结果并不是很精确;

  • 模糊滤镜比较消耗性能,如果在页面上大量使用的话,可能会对应用的性能造成影响。

很明显 CSS 方案虽然实现起来比较简单,但并不是最好的方案。既然 CSS 方案行不通,那么我们就把目光转向 JS 方案。功夫不负有心人,在全球最大的程序员交流平台上,我找到了 color-thief 这个项目。该项目通过 JS 实现了从图片中获取调色板的功能,同时支持浏览器和 Node.js 环境。

c6f4973eec8a7b33b87595fabc00938e.png

(图片来源:https://lokeshdhakar.com/projects/color-thief/)

上图是 color-thief 项目提供的在线示例,提取图片调色板的效果,图中的 Dominat Color 表示图片的主题色。经过一番测试,发现该库提取图片调色板的功能还是挺不错的,那时心里的第一感觉就是就用它了。之后,我就开始在脑海中回顾产品经理提的需求。

我司 App 首页的 Banner 区,可以配置多张不同的 Banner 图,这些图会以轮播的形式展示。因为图片使用的是线上的地址,在轮播到下一张图片的时候,Banner 区的背景就要立即发生变化。如果在浏览器端进行解析的话,就没有办法及时切换背景色了,特别对于较大的图片来说,可能会导致背景色切换延迟。因为这些问题,我们只能考虑在服务端进行图片主题色提取了。心想幸亏 color-thief 也支持 Node.js 环境,不过没过几秒,就发现情况不对了。我们 App 首页上的 Banner 图片,在管理后台上传的时候,是直接传到七牛云 CDN 的,不是上传到我司的服务器。

8a1277794bb0deb58c1d138f417ffcd7.png

难道为了开发这个功能,我们要调整 Banner 图片的上传方式?直觉告诉我,这种方案肯定不太合适。那么应该如何处理呢?要不翻翻七牛云的官方文档,看能不能找到一些有用的信息。不看不知道,一看吓一跳。七牛云智能多媒体服务 竟然为了开发者提供了十几种的图片处理功能:

  • 图片瘦身

  • 图片压缩

  • 图片水印

  • 图片盲水印

  • 图片 EXIF 信息

  • 图片圆角处理

  • 图片平均色调

  • 动图合成

  • 图片全景拼接

  • 图片样式

  • ...

那时我第一眼就看中了 图片平均色调 这个功能,该功能的介绍如下图所示:

963e0008cb5831ba59b6390ec760f455.png

(图片来源:https://developer.qiniu.com/dora/1268/image-average-hue-imageave)

使用起来也很简单,直接在七牛云图片资源后面添加 imageAve 查询参数即可,成功请求之后就会以 JSON 的形式返回图片的 RGB 信息:

{"RGB": "0x85694d"
}

三、功能实现

之后,我测试了多张图片并把测试结果反馈给产品经理。在产品经理确认之后,我就开始着手开发上述功能。不过在具体开发前,我对 Banner 图片的处理过程进行了梳理,具体的流程如下图所示:

e89c11ad11936cf3c34cf53102047be5.png

在经过上述的流程处理后,我们就可以取得每张 banner 图片对应的 url 地址和 rgb 图片平均色调的值。有了这些信息之后,我们就只要在 banner 图片切换的时候,同步更新该 banner 图片对应背景色即可。为了提高用户的视觉体验,我们对背景色进行了渐变处理。

在看具体的核心代码之前,我们先来看一下实际的运行效果:

32540c10cc6c107cb962c470e6d6b49b.gif

看完上述的实际效果之后,接下来我们来简单介绍一下相关代码。

图片数据

// 实际项目中,该数据是从服务端接口中获取
const images = [{url: "https://***.jpg", // 七牛云图片地址rgb: "0xa28c60", // 当前图片对应的平均色调},...{url: "https://***.jpg",rgb: "0x98aea5",},
];

Vue 页面模板

<template><div class="block"><div class="banner-bg" :style="{ 'background-image': bgColor }"></div><el-carousel@change="changeCard"trigger="click"height="150px":initial-index="initialIndex"v-if="bannerImages.length > 0"><el-carousel-item v-for="(image, index) in bannerImages" :key="index"><img class="slide-img" :src="image.url" /></el-carousel-item></el-carousel></div>
</template>

Vue 逻辑代码

export default defineComponent({name: "App",data() {return {bannerImages: [], // Banner上要显示的图片列表initialIndex: 0, // 初始索引值bgColor: "none", // 默认背景颜色};},mounted() {this.bannerImages = images.map((image, index) => {const bannerImage: Record<string, any> = { ...image };const bannerBgColor = image.rgb.slice(2, 8); // 获取banner背景色const nextBannerBgColor =index === images.length - 1? images[0].rgb.slice(2, 8): images[index + 1].rgb.slice(2, 8);bannerImage.startRgb = hex2Rgb(`#${bannerBgColor}`);bannerImage.endRgba = hex2Rgb(`#${bannerBgColor}`, 0.8);bannerImage.gradientColor = gradientColors( // 生成渐变颜色`#${bannerBgColor}`,`#${nextBannerBgColor}`,5);const rgbArr = hex2Rgb(`#${bannerBgColor}`, null, true);bannerImage.lightNess =(rgbArr[0] * 0.2126 + rgbArr[1] * 0.7152 + rgbArr[2] * 0.0722) / 255;if (this.initialIndex === index) {this.bgColor = `linear-gradient(-180deg, ${bannerImage.startRgb} 20%, ${bannerImage.endRgba} 98%)`;}return bannerImage;});},methods: {changeCard(index) {this.bgColor = `linear-gradient(-180deg, ${this.bannerImages[index].startRgb} 20%, ${this.bannerImages[index].endRgba} 98%)`;},},
});

在以上代码中,我们使用了 hex2RgbgradientColors 两个工具函数,它们分别用来把十六进制的颜色转成 RGB/RGBA 的格式和生成渐变颜色。它们的具体实现如下所示:

hex2Rgb 函数

export function hex2Rgb(color, opacity?: number, getRgbList?: boolean) {const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;let sColor = color.toLowerCase();if (sColor && reg.test(sColor)) {if (sColor.length === 4) {let sColorNew = "#";for (let i = 1; i < 4; i += 1) {sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));}sColor = sColorNew;}//处理六位的颜色值const sColorChange = [];for (let i = 1; i < 7; i += 2) {sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2)));}if (getRgbList) {return sColorChange;}return typeof opacity !== "undefined"? `RGBA(${sColorChange.join(",")},${opacity})`: `RGB(${sColorChange.join(",")})`;} else {return sColor;}
}

gradientColors 函数

// 计算两个颜色之间渐变值
export function gradientColors(startColor: string,endColor: string,step: number
) {const startRGB = colorRgb(startColor); //转换为rgb数组模式const startR = startRGB[0];const startG = startRGB[1];const startB = startRGB[2];const endRGB = colorRgb(endColor);const endR = endRGB[0];const endG = endRGB[1];const endB = endRGB[2];const sR = (endR - startR) / step; //总差值const sG = (endG - startG) / step;const sB = (endB - startB) / step;const colorArr = [];for (let i = 0; i < step; i++) {//计算每一步的hex值const hex = colorHex("rgb(" +parseInt(sR * i + startR) +"," +parseInt(sG * i + startG) +"," +parseInt(sB * i + startB) +")");colorArr.push(hex);}return colorArr;
}

利用这个图片处理功能,最终完成了我司产品经理提出的需求。通过这次功能的开发,我对图片主题色提取算法也有一定的了解。另外,除了图片处理,七牛云智能多媒体服务还提供了 盲水印处理动图合成 和 图片全景拼接 等挺好玩的功能,感兴趣的小伙伴可以体验一下,点击阅读原文即可跳转到官网页面~

四、参考资源

  • 小技巧!CSS 提取图片主题色功能探索

  • 如何对前端图片主题色进行提取?这篇文章详细告诉你

6a05767f54a3b321ed606d0f2879bb9c.gif

1. JavaScript 重温系列(22篇全)

2. ECMAScript 重温系列(10篇全)

3. JavaScript设计模式 重温系列(9篇全)

4. 正则 / 框架 / 算法等 重温系列(16篇全)

5. Webpack4 入门(上)|| Webpack4 入门(下)

6. MobX 入门(上) ||  MobX 入门(下)

7. 120+篇原创系列汇总

2d564fce631cde95f2d4790941266a62.gif

回复“加群”与大佬们一起交流学习~

点击“阅读原文”查看 120+ 篇原创文章

http://www.xdnf.cn/news/11448.html

相关文章:

  • 乐趣无限:10款基于Pygame的经典游戏合集
  • 外呼系统:实现精准营销的关键
  • com 组件调用不起来_Spring Cloud Alibaba,分布式服务调用(四)
  • 银行数据分析进阶篇:银行业零售贷款营销与风控平衡分析
  • JavaSE入门:初识java
  • 八款最优秀的免费翻译相关软件推荐
  • 3.3 matlab用switch语句实现选择结构
  • sql基本语句和关键字
  • 【OushuDB】Oushu Database 简介
  • VMware+CentOS7搭建私有云桌面服务
  • 推荐4款功能强悍的软件,好用到你无可挑剔,快收藏起来偷偷使用
  • 惩罚女人的最有效方法!
  • Android手机上使用Socks5全局代理-教程+软件
  • 如何登陆java版的微信_Java对接微信登录
  • 深度解析自动化测试流程(纯干货)
  • 同步推正版授权的原理
  • UI设计案例,B端后台界面设计教程
  • 韩寒等50名作家3.15联袂声讨百度侵权
  • Swift—Cocoa Touch设计模式-备
  • 快播5.0永不升级版 孤雨定制骨头版
  • 洛克人java下载_洛克人威利博士之谜
  • [Python 爬虫]煎蛋网 OOXX 妹子图爬虫(1)——解密图片地址
  • 硬盘安装Opensuse11.1
  • 聊聊电商系统架构演进(图解版)
  • 王者荣耀服务器维护到什么时间,王者荣耀维护到几点结束?ios6.23维护更新结束时间一览...
  • 进程通信——管道和命名管道
  • 硬盘无法分区的原因以及3种解决方法!
  • Python OCR库:自动化测试验证码识别神器!
  • symbian系统开发教程(一)
  • 神秘“鬼影”病毒袭击Winxp系统,重装也无法消灭