产品:“写个banner这么费劲?”
前端瓶子君,关注公众号
回复算法,加入前端编程面试算法每日一题群
上午产品提了个“简单”的banner开发的需求,本来以为很简单,一不小心消耗了我一天的时间,一时间感慨万千,来掘金记录一下。写这篇文章,除了技术经验之外,还想给手机前的你一点提醒:程序从来都不是完美的,但请对那些追求完美的程序员(自己)保持着敬畏。
什么需求?
产品:在我们项目用户中心中搞个banner,展示用户上传的图片,要完整展示图片。
我:可能有些图片尺寸跟banner的盒子不一致,即使图片缩放,也会导致上下或者左右有留边的现象,可以接受吗?
产品:可以,留黑边即可
我:黑边?
产品:黑色版用黑边,白色版用白边
我:哦
然后,我就开始想怎么做了!
我打算怎么做?
鉴于以上对白很朴素,很有必要把banner的情况列一下:
新上传的banner图比例是固定比例剪切的
之前上传的是完整图片,比例是不固定的。
我的第一个想法是优先新banner,老banner留边即可:
假如现在banner的比例是2:1
,用三张不同比例的图片(第一个是图片比例刚好是2:1,第二个比例更小,第三个比例更大)测试的结果:
跟预期的一样,也符合产品需求,不过我不想就此停步,能否调和一下留边问题呢?我陷入了深思...
处理留边
首先想到的是增加底色,来个#ccc
试试:
感觉好一些了,但颜色值是固定死的,跟图片不太协调,能否从图片里取一个主色作为底色呢?这样就不会这么突兀了,我想起来了自己之前的一篇文章将彩色图片转成黑白——纯前端实现[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%`
复制代码
这里再多选几张图片看效果:
最后
其实,除了背景图+固定比例还有其他的方案,比如通过动态获取图片的尺寸,配合其他参数(如banner允许的最大宽度和高度)可以计算出一个合适的容器尺寸。可配置性更高,但是开发成本也会高一些。
最后,再来个好玩的东西--web颜色拾取器:
仓库地址在这里:github地址[2]:https://github.com/anderlaw/color-picker-demo
来自:AndyLaw
https://juejin.cn/post/7068590848228720671
最后
欢迎关注【前端瓶子君】✿✿ヽ(°▽°)ノ✿
回复「算法」,加入前端编程源码算法群,每日一道面试题(工作日),第二天瓶子君都会很认真的解答哟!
回复「交流」,吹吹水、聊聊技术、吐吐槽!
回复「阅读」,每日刷刷高质量好文!
如果这篇文章对你有帮助,「在看」是最大的支持
》》面试官也在看的算法资料《《
“在看和转发”就是最大的支持
产品:“写个banner这么费劲?”相关推荐
- 刚接触js不久,自己写的banner幻灯片效果。
对于我这种菜鸟来讲,刚接触项目.叫我用插件,其实我说插件太臃肿不想用,倒不如说我是看不懂那些插件...- -(更愿意自己写点看得懂的代码,顺便也是个学习的过程) 所以自己花了些时间,自己来写了个dem ...
- 怎么写文案?新产品文案
这篇文章,我之前是有看过的,收获颇丰.最近弄一个新产品文案,又翻出来认真研读了一下,真的是特别合适抓破脑袋想写好新品文案的人,很适用.引荐给大家,用起来. 当你想卖一瓶200元的洗发水,产品文案第一 ...
- 产品经理如何写产品的招标参数
作为产品经理,如何为自己的产品写一份便于销售投标用的招标参数呢?下面展示一下我最近写的一份<xx产品招标参数>,由于部分内容涉及隐私,就省去敏感部份,以简单版作为展示吧. 指标项 指标要求 ...
- 写单元测试的好处(转)
许多开发者都有个习惯,常常不乐意去写个简单的单元测试程序来验证自己的代码.对自己的程序一直非常有自信,或存在侥幸心理每次运行通过后就直接扔给测试组测试了.然而每次测试组的BUG提交过来后就会发现自己的 ...
- 【教你如何放大招】程序猿是如何一点点的吃下一个产品的
本文宗旨 目标 : 从一个程序猿的角度来解读如何消化掉产品经理给的需求 即产品经理放一个大招 程序猿如何接大招 适读人群 : 1⃣️ 刚入行的小白:对开发的流程还不太熟悉 接到一个需求 一头乱码 一头 ...
- 花三千万写出的十个 to B创业大坑( 下)
常垒资本聚焦to B早期创业投资,我们这篇文章献给准备以及正在创业的朋友们.阅读之前欢迎先阅读上篇.中篇~(点击下方图片即可跳转文章) 作者:威努特 龙国东 编辑:常垒资本 冯斯基 捌 骗子 整 ...
- 深度干货 | 双十一电商大促流量承接与变现的产品设计分析
2015年的双十一电商大促已经谢幕,回想起除了那些高到令人咂舌的数据以外,是否还有那些铺天盖地而来的广告宣传,这是千亿成交的第一步,曝光与流量:下一步就是流量承接的产品页面,如:大促会场.SNS活动. ...
- 初入产品一直坚持做的事情
产品的内日分享 初入产品行业,没有什么产品的积累,在做业务功能的时候,甚至不知道一个图标该怎么放置,这个功能放在那里好呢,脑袋里面空空如也.所以初入产品行业的时,我没有上来就直接去做产品,而是要多看. ...
- 写文章一年了,我用到的工具都在这儿了
好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star ⭐⭐⭐⭐⭐转载请注明出处!⭐⭐⭐⭐⭐ 链接:https:/ ...
最新文章
- 大型AI已有自主意识了?LeCun开喷Open AI首席科学家
- 2022年最新限量红包封面,炸裂的不要不要的!速度领取!
- WYSE率先支持RemoteFX功能
- 满天星_Java实例_源码+图片素材
- Segment fault及LINUX core dump详解
- Java并发包中Semaphore的工作原理、源码分析及使用示例
- linux虚拟机上lvs-nat的实现
- C++--第13课 - 操作符重载 - 下
- Odoo CRM获福布斯评为《2022最佳开源CRM》
- 轻量锁volatile
- 百度统计热力图邀请码
- 【数据结构】- 教你一步完美应对面试官让你10分钟内实现带头双向循环链表(下)
- 兄弟hl3150cdn打印测试页6_兄弟3150CDN彩色激光打印机
- Android开发 ConstraintLayout布局的详解
- 管理学二(学习与沟通的重要性)
- Golang神奇的2006-01-02 15:04:05
- 店铺信息html,编辑店铺信息.html
- Android实现实时视频聊天功能|源码 Demo 分享
- minio分布式解决方案
- PHP composer 设置代理