当青训营遇上码上掘金
零、优雅的自我介绍
作为一名程序员,咱怎么进行优雅地自我介绍?
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
greet() {
alert('hello')
}
}
这样?这只是简单的面向对象编程罢了,一点都不酷,一点都不优雅,只要有点代码基础就能够写出来。
作为一名程序员,会装是一门必修课,而制作出自己一份漂漂亮亮的名片,则是会装的基础。这让我回想起在某网站上使用字符画拼出自我介绍的名片,同时也在同学使用Springboot的时候见过类似的字符画:
像这样。
如果能根据一张图片,用代码自己生成一幅字符画,绝对是自我介绍的绝佳名片!
二、实现原理
首先准备一组排好序的不同 着色密度 的ascii字符(事实上你可以用任何字符),接着将源图转为灰度图,然后遍历图中的像素,根据r/g/b通道的值来匹配字符串中相应着色密度的字符,值越小则颜色越深,字符的“密度”也应越大。
如果需要保留颜色,只需将灰度图和原图的像素位置一一对应即可。
在开始实现功能之前,我们需要先了解一下颜色矩阵(ColorMatrix)。在计算机中,每个像素的颜色可以用一个向量矩阵表示:[R, G, B, A]。颜色变换矩阵通常是用一个 5x5 的矩阵来表示,和空间中一个 n 维向量的平移变换需要用一个 n+1 维的矩阵来表示一样,颜色矩阵也需要引入一个齐次坐标来进行「平移操作」。
三、前期准备
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>ascii art</title>
<style>
* {
margin: 0;
padding: 0;
}
canvas, img, #container {
display: block;
margin: auto;
}
#container {
line-height: 12px;
font-size: 12px;
font-family: 'SimHei', monospace;
letter-spacing: 6px;
}
</style>
</head>
<body>
<img src="https://i.postimg.cc/mrFwCvBG/name.png"/>
<div id="container"></div>
<script>
(function () {
// 这里是js代码
})()
</script>
</body>
</html>
先准备好一个容器,并在互联网上上传自己需要的图片。后期,通过js代码实现图片转字符画功能
四、核心功能实现
var container = document.getElementById('container')
var offScreenCvs = document.createElement('canvas') // 创建离屏canvas
var offScreenCtx = offScreenCvs.getContext('2d', { alpha: false }) // 关闭透明度
var offScreenCvsWidth, offScreenCvsHeight
var samplerStep = 4 // 采样间隔
var img = new Image()
var onImgLoaded = function () {
offScreenCvsWidth = img.width
offScreenCvsHeight = img.height
offScreenCvs.width = offScreenCvsWidth
offScreenCvs.height = offScreenCvsHeight
offScreenCtx.drawImage(img, 0, 0, offScreenCvsWidth, offScreenCvsHeight)
imageData = offScreenCtx.getImageData(0, 0, offScreenCvsWidth, offScreenCvsHeight)
// 采样点数 = 图片宽度 / 采样间隔;容器边长 = 采样点数 × 字体大小
container.style.width = (offScreenCvsWidth / samplerStep * 12) + 'px'
container.style.height = (offScreenCvsHeight / samplerStep * 12) + 'px'
render()
}
img.src = './trump.png'
img.complete ? onImgLoaded() : (img.onload = onImgLoaded) // 确保onImgLoaded被执行
var imageData
var x, y, pos
var asciiCharArray = '#KDGLftji+;,:.'.split('') // 准备不同密度的字符数组(降序)
var durationPerChar = Math.ceil(255 / asciiCharArray.length) // 每个字符代表的密度阈值
function render () {
var imageDataContent = imageData.data
var strArray = []
var part1, part2
var letter
var value
for (y = 0; y < offScreenCvsHeight; y += samplerStep) {
strArray.push('<p>') // 使用P标签换行
for (x = 0; x < offScreenCvsWidth; x += samplerStep) {
pos = y * offScreenCvsWidth + x
// 获取RBG加权平均后的灰度值
value = imageDataContent[pos * 4] * 0.3086 + imageDataContent[pos * 4 + 1] * 0.6094 + imageDataContent[pos * 4 + 2] * 0.0820
imageDataContent[pos * 4] = imageDataContent[pos * 4 + 1] = imageDataContent[pos * 4 + 2] = value
// 判断灰度值落在那个密度范围中,拿到对应的字符
part1 = Math.floor(value / durationPerChar)
part2 = value % durationPerChar
letter = part2 ? asciiCharArray[part1] : (part1 ? asciiCharArray[part1 - 1] : 'æ')
strArray.push(letter)
}
strArray.push('</p>')
}
container.innerHTML = strArray.join('')
}
解释一下onImgLoaded核心函数:
通常来说img.onload必须要放在 img.src之前,来保证onload回调一定会执行,否则的话如果图片在执行这段代码之前已经被浏览器缓存了,则有可能不会触发onload回调。
但是有时候由于业务的需要,有些操作必须要在图片载入完成后执行,可是不一定立即执行,碰到这种情况,就可以用到Image对象的complete属性,该属性会返回当前图片是否加载完成的bollean值。
于是,通过上面这行代码,就可以确保onImgLoaded函数在图片载入完成后一定会被触发。