前端瓶子君,关注公众号

回复算法,加入前端编程面试算法每日一题群

上午产品提了个“简单”的banner开发的需求,本来以为很简单,一不小心消耗了我一天的时间,一时间感慨万千,来掘金记录一下。写这篇文章,除了技术经验之外,还想给手机前的你一点提醒:程序从来都不是完美的,但请对那些追求完美的程序员(自己)保持着敬畏。

什么需求?

产品:在我们项目用户中心中搞个banner,展示用户上传的图片,要完整展示图片。

我:可能有些图片尺寸跟banner的盒子不一致,即使图片缩放,也会导致上下或者左右有留边的现象,可以接受吗?

产品:可以,留黑边即可

我:黑边?

产品:黑色版用黑边,白色版用白边

我:哦

然后,我就开始想怎么做了!

我打算怎么做?

鉴于以上对白很朴素,很有必要把banner的情况列一下:

  • 新上传的banner图比例是固定比例剪切的

  • 之前上传的是完整图片,比例是不固定的。

我的第一个想法是优先新banner,老banner留边即可:

假如现在banner的比例是2:1,用三张不同比例的图片(第一个是图片比例刚好是2:1,第二个比例更小,第三个比例更大)测试的结果:

跟预期的一样,也符合产品需求,不过我不想就此停步,能否调和一下留边问题呢?我陷入了深思...

处理留边

首先想到的是增加底色,来个#ccc试试:

image.png

感觉好一些了,但颜色值是固定死的,跟图片不太协调,能否从图片里取一个主色作为底色呢?这样就不会这么突兀了,我想起来了自己之前的一篇文章将彩色图片转成黑白——纯前端实现[1]。

我决定取两个主色,一个是左半区的,一个是右半区的,然后用这俩颜色搞个渐变作为底色:

看起来还凑合,至少比之前好多了,接下来介绍相关技术细节和代码实现。

技术细节

首先如何给一个容器设置宽高比呢?

  • 通过padding-top设置对应的百分比值

  • 通过新属性aspect-ratio(safari不支持)

这里,为了兼容性,我用的第一种。设置一个长宽比为2:1的盒子:

div{background-size: contain;background-repeat: no-repeat;background-position: center;/* 2:1 ,panding百分比值是相对于盒子的宽度的*/padding-top: 50%;
}
复制代码

再次,如何获取图片的主色呢?我们借助Canvas的ctx.getImageData方法。

分一下几个步骤:

  • 将图片绘制到一个canvas元素上

  • 获取图像所有的rgba像素点

  • 取某个区域颜色的均值,并找出这个区域最接近均值的rgba颜色,作为该区域的主色

var imgSrc = "XXXXX"
const imgEle = document.createElement('img')
const canvas = document.createElement('canvas')
imgEle.src = imgSrc
imgEle.onload = () => {var ctx = canvas.getContext("2d");var naturalImgSize = [imgEle.naturalWidth, imgEle.naturalHeight];canvas.width = naturalImgSize[0];canvas.height = naturalImgSize[1];//绘制到canvasctx.drawImage(imgEle, 0, 0);//获取imageData:rgba像素点var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);const leftSectionData = []const rightSectionData = []const oneLineImgDataLen = canvas.width * 4;imgData.data.forEach((colorVal, i) => {if (i % onelineImgDataLen <= 0.5 * onelineImgDataLen || i % onelineImgDataLen >= 0.6 * onelineImgDataLen) {const inLeft = i % onelineImgDataLen <= 0.5 * onelineImgDataLenif (i % 4 === 0) {// 获取rgb均值const curAverageRGB = (imgData.data[i] + imgData.data[i + 1] + imgData.data[i + 2]) / 3;let leftOrRightRef = inLeft ? leftSectionData : rightSectionData;//每个数组里存四个值:本颜色值中的r、g、b的均值,以及r、g、b三个值。//均值一方面用于累加计算本区域的整体均值,然后再跟每个均值对比拿到与整体均值最接近的项的索引,再取该数组里的后三个值:rgb,对应着颜色leftOrRightRef[leftOrRightRef.length] = [curAverageRGB, imgData.data[i], imgData.data[i + 1], imgData.data[i + 2]]}}})//generate average rgbconst averageOfLeft = Math.round(leftSectionData.reduce((_cur, item) => {return _cur + item[0]}, 0) / leftSectionData.length)const averageOfRight = Math.round(rightSectionData.reduce((_cur, item) => {return _cur + item[0]}, 0) / rightSectionData.length)//find the most near colorconst findNearestIndex = (averageVal, arrBox) => {let _gapValue = Math.abs(averageVal - arrBox[0])let _nearColorIndex = 0arrBox.forEach((item, index) => {const curGapValue = Math.abs(item - averageVal)if (curGapValue < _gapValue) {_gapValue = curGapValue_nearColorIndex = index}})return _nearColorIndex}const leftNearestColor = leftSectionData[findNearestIndex(averageOfLeft, leftSectionData)]const rightNearestColor = rightSectionData[findNearestIndex(averageOfRight, rightSectionData)]console.log(leftNearestColor,rightNearestColor)
}
复制代码

取到颜色,实现元素的渐变:

element.style.backgroundImage = `url("XXXX"),linear-gradient(90deg,rgba(${leftNearestColor[1]},${leftNearestColor[2]},${leftNearestColor[3]},1) 0%,rgba(${rightNearestColor[1]},${rightNearestColor[2]},${rightNearestColor[3]},1) 100%`
复制代码

这里再多选几张图片看效果:

image.png

最后

其实,除了背景图+固定比例还有其他的方案,比如通过动态获取图片的尺寸,配合其他参数(如banner允许的最大宽度和高度)可以计算出一个合适的容器尺寸。可配置性更高,但是开发成本也会高一些。

最后,再来个好玩的东西--web颜色拾取器:

仓库地址在这里:github地址[2]:https://github.com/anderlaw/color-picker-demo

来自:AndyLaw

https://juejin.cn/post/7068590848228720671

最后

欢迎关注【前端瓶子君】✿✿ヽ(°▽°)ノ✿

回复「算法」,加入前端编程源码算法群,每日一道面试题(工作日),第二天瓶子君都会很认真的解答哟!

回复「交流」,吹吹水、聊聊技术、吐吐槽!

回复「阅读」,每日刷刷高质量好文!

如果这篇文章对你有帮助,「在看」是最大的支持

》》面试官也在看的算法资料《《

“在看和转发”就是最大的支持

产品:“写个banner这么费劲?”相关推荐

  1. 刚接触js不久,自己写的banner幻灯片效果。

    对于我这种菜鸟来讲,刚接触项目.叫我用插件,其实我说插件太臃肿不想用,倒不如说我是看不懂那些插件...- -(更愿意自己写点看得懂的代码,顺便也是个学习的过程) 所以自己花了些时间,自己来写了个dem ...

  2. 怎么写文案?新产品文案

    这篇文章,我之前是有看过的,收获颇丰.最近弄一个新产品文案,又翻出来认真研读了一下,真的是特别合适抓破脑袋想写好新品文案的人,很适用.引荐给大家,用起来. ​当你想卖一瓶200元的洗发水,产品文案第一 ...

  3. 产品经理如何写产品的招标参数

    作为产品经理,如何为自己的产品写一份便于销售投标用的招标参数呢?下面展示一下我最近写的一份<xx产品招标参数>,由于部分内容涉及隐私,就省去敏感部份,以简单版作为展示吧. 指标项 指标要求 ...

  4. 写单元测试的好处(转)

    许多开发者都有个习惯,常常不乐意去写个简单的单元测试程序来验证自己的代码.对自己的程序一直非常有自信,或存在侥幸心理每次运行通过后就直接扔给测试组测试了.然而每次测试组的BUG提交过来后就会发现自己的 ...

  5. 【教你如何放大招】程序猿是如何一点点的吃下一个产品的

    本文宗旨 目标 : 从一个程序猿的角度来解读如何消化掉产品经理给的需求 即产品经理放一个大招 程序猿如何接大招 适读人群 : 1⃣️ 刚入行的小白:对开发的流程还不太熟悉 接到一个需求 一头乱码 一头 ...

  6. 花三千万写出的十个 to B创业大坑( 下)

    常垒资本聚焦to B早期创业投资,我们这篇文章献给准备以及正在创业的朋友们.阅读之前欢迎先阅读上篇.中篇~(点击下方图片即可跳转文章) 作者:威努特    龙国东 编辑:常垒资本 冯斯基 捌 骗子 整 ...

  7. 深度干货 | 双十一电商大促流量承接与变现的产品设计分析

    2015年的双十一电商大促已经谢幕,回想起除了那些高到令人咂舌的数据以外,是否还有那些铺天盖地而来的广告宣传,这是千亿成交的第一步,曝光与流量:下一步就是流量承接的产品页面,如:大促会场.SNS活动. ...

  8. 初入产品一直坚持做的事情

    产品的内日分享 初入产品行业,没有什么产品的积累,在做业务功能的时候,甚至不知道一个图标该怎么放置,这个功能放在那里好呢,脑袋里面空空如也.所以初入产品行业的时,我没有上来就直接去做产品,而是要多看. ...

  9. 写文章一年了,我用到的工具都在这儿了

    好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star ⭐⭐⭐⭐⭐转载请注明出处!⭐⭐⭐⭐⭐ 链接:https:/ ...

最新文章

  1. 大型AI已有自主意识了?LeCun开喷Open AI首席科学家
  2. 2022年最新限量红包封面,炸裂的不要不要的!速度领取!
  3. WYSE率先支持RemoteFX功能
  4. 满天星_Java实例_源码+图片素材
  5. Segment fault及LINUX core dump详解
  6. Java并发包中Semaphore的工作原理、源码分析及使用示例
  7. linux虚拟机上lvs-nat的实现
  8. C++--第13课 - 操作符重载 - 下
  9. Odoo CRM获福布斯评为《2022最佳开源CRM》
  10. 轻量锁volatile
  11. 百度统计热力图邀请码
  12. 【数据结构】- 教你一步完美应对面试官让你10分钟内实现带头双向循环链表(下)
  13. 兄弟hl3150cdn打印测试页6_兄弟3150CDN彩色激光打印机
  14. Android开发 ConstraintLayout布局的详解
  15. 管理学二(学习与沟通的重要性)
  16. Golang神奇的2006-01-02 15:04:05
  17. 店铺信息html,编辑店铺信息.html
  18. Android实现实时视频聊天功能|源码 Demo 分享
  19. minio分布式解决方案
  20. PHP composer 设置代理

热门文章

  1. Dreamweaver自带流体布局+自己添加,后附效果,不知代码有错误没?请行家指正!多谢先
  2. ECCV2022 论文最全汇总!附下载
  3. P1282 多米诺骨牌题解
  4. 多角度解析特斯拉电动车技术到底领先了多少?|厚势
  5. 免费linux虚拟空间,linux免费虚拟主机(linux搭建虚拟主机)
  6. R语言使用倾向评分提高RCT(随机对照试验)的效率
  7. python五子棋课程设计报告_算法课程设计:使用Python完成可视化的五子棋AI
  8. 使用阿里云提供的短信服务发送短信(个人版)
  9. pytorch super 的用法
  10. 如何下载西门子产品CAD、3D和EPLAN文件