作者归档:白宦成

关于白宦成

独立开发者, 自由职业者, 写作者

red yellow and green paper

如何判断一个颜色是什么颜色?

当你看到这个题目的时候,你可能会有点迷惑:“一个颜色是什么颜色”,这个问题好无厘头。但如果我换个用法 ,你可能就能明白 — 人类是如何辨别出一个颜色是红色而不是绿色?

回想小时候,大家应该都听说过 —— 三原色。不同的人可能记得不同,有的人记得是红绿蓝(色光三原色),也有人记得是红青黄(美术三原色)。结果不一样,但并不影响。两种三原色都告诉你了 —— 一个颜色是可以由另外三种颜色组合而成。

这意味着,每一个颜色都对应着三个坐标。他们是 RGB 也好,还是其他也好,都是通过三个颜色的色码来确认是哪个具体的颜色的。我们的显示器也是如此制造的。

但三原色有个问题 —— 变量太多。假设我们在每个维度分 3 个不同的结果,三原色可以拼出 27 种不同的组合。如果我们在每个维度分成 10 阶,这个结果就是 1000 个不同的维度。按照 RGB的 0 ~ 255 ,一共 256 阶,则一共可以形成 16,777,216 个组合。如果你想要构建出一组用于判断的规则,就十分的困难。

因此,换一个坐标系会是更好的办法 —— 试试 HSL 描述方法。

HSL 是将 RGB 三维坐标转换为色相、饱和度、亮度(英语:Hue, Saturation, Lightness)组成的新维度坐标。

d2b5ca33bd970f64a6301fa75ae2eb22 2

当我们变为新的坐标系后,我们定义颜色的坐标就从过去的三个维度,变为了只需要色相(Hue)这一个坐标就可以定位的坐标。毕竟,如果一个蓝色不饱和饱和,都不影响他是个蓝色;同样的,一个蓝色的亮度不高,但依然是蓝色。

通过从 RGB 坐标系转变为 HSL 坐标系,我们就将过去的 16,777,216 个组合简化为 360 阶。从过去的三维坐标系,变为了全新的一维坐标系。

d2b5ca33bd970f64a6301fa75ae2eb22 3

这样我们就可以十分简单的来细分我们的不同的颜色,更好的来编写规则。比如Hue 在 15 ~ 45 时为橙色;在45 ~ 75 时为黄色;在 75 ~105 时为绿色等等。

在我们写代码的时候也会更加简单,我们只需要计算出具体的 Hue 的值,就可以得出对应的颜色的名字。

RGB to HSL 算法

如下算法来自 Wikipedia

https://zh.wikipedia.org/wiki/HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4

设 (rgb)分别是一个颜色的红、绿和蓝坐标,它们的值是在0到1之间的实数。设max等价于rgb中的最大者。设min等于这些值中的最小者。要找到在HSL空间中的 (hsl)值,这里的h ∈ [0, 360)是角度的色相角,而sl ∈ [0,1]是饱和度和亮度,计算为:14aa645507f35455bbc990e04ecc0907128c509a

fffb33944b2529ac9398bfb365968795a03b3ddc{\frac {1}{2}}\end{cases}}”>

l={\begin{matrix}{\frac  {1}{2}}\end{matrix}}(max+min)

h的值通常规范化到位于0到360°之间。而h = 0用于max = min的(定义为灰色)时候而不是留下h未定义。

RGB to HSL Sample Code

function rgbToHsl(r, g, b) {
  r /= 255, g /= 255, b /= 255;

  var max = Math.max(r, g, b),
    min = Math.min(r, g, b);
  var h, s, l = (max + min) / 2;

  if (max == min) {
    h = s = 0; // achromatic
  } else {
    var d = max - min;
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

    switch (max) {
      case r:
        h = (g - b) / d + (g < b ? 6 : 0);
        break;
      case g:
        h = (b - r) / d + 2;
        break;
      case b:
        h = (r - g) / d + 4;
        break;
    }

    h /= 6;
  }

  return [h, s, l];
}
Code language: JavaScript (javascript)

延展阅读

https://www.december.com/html/spec/colorterms.html

summary

2022 年 7 月月度总结

Objective 1:持续获取现金流,并构建未来收益的现金牛

KR1:投资收益达到 20000 元

之前跌掉的浮盈又双叒叕涨回来了。还是挺开心的。

KR2 :单篇稿费突破 6000 元

没有变化。不过看到了一个新的观点:

先报价,提供符合对应价格的产出;不断循环,并提供价值。

下个月试试看。

KR3 :达成年度预算,支出不超预算

换了新预算模型之后,并调整了计算后,发现我的 7 月的基本开支大概是在 8000 左右,看起来还是比较可控。目前的住房成本还是比较高的。后续继续观察。如果控制的好的话,一年成本也就是 10 万以内。

d2b5ca33bd970f64a6301fa75ae2eb22 1

KR4 :构建软件类现金牛业务,预期产生收益 10000 元人民币m

无进展。

Objective 2:提升生活基础设施,构建未来生活好基础

KR1:前往 6 个城市旅行

疫情。。。不出门。

KR2:进行 20 次文娱活动

主要是在家看书,倒没怎么出门。

KR3:借助智能化设备,缩减在家务相关事务上耗费的时间

暂无进展

Objective 3 :开拓视野,打造多元行业人才

KR1:写 15 篇书评

本月两篇书评

KR2:输出关于加密货币的 Newsletter 12 封

暂无进展。但最近开始继续用 Heptabase,希望早日输出成功!

KR3 :完成计划中的三本图书的写作

暂无进展。

6ee6df690137fd06bc6166adb63caca1

使用小程序的 Canvas 2D 提取特定点的颜色

在小程序当中,我们可以通过在 Canvas 画布上绑定 Tap 事件,来获取到用户点击行为,当我们获取到点击行为对应的坐标时,就可以读取到对应位置的颜色。并根据我们的需求,将颜色转换成我们想要的 RBG 色值。

这就是我们常见的各种取色软件的最常见的实现方式。当然,落实到实际开发过程中,大家或多或少会有所不同(涉及到调色。我们拍照的颜色和我们所看到的颜色并不完全一样。不同的相机调色会有所不同。类似的,我们需要加入相应的逆向算法排除对应的相机自身调教的影响)。

在小程序的场景中具体实现方式可参考如下代码:

页面布局

<view>
  <view><button bindtap="chooseImage">1. 选择图片</button></view>
  <view><canvas id="myCanvas" bindtap="onCanvasTap" type="2d" style="background-color:gray;width: 100%;margin-top: 100rpx;"></canvas></view>
</view>
Code language: HTML, XML (xml)

对应的 JS

和在 在小程序中使用的 Canvas 2D API 绘制本地图片 一样,你需要提取到 Canvas 的 Ctx ,用于执行操作。当你通过 bindTap 拿到你的点击点相对于 Canvas 的 左上角的坐标后,就可以使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/getImageData">getImageData</a> 来提取具体范围的数据。因为我的目的是提取某个点的颜色,因此我的第三、第四参数都是 1 (取一个像素点的宽度和高度)。

提取出图片数据后,你可以在 data 当中提取一个 4 个元素的数组,按照顺序,分别是 RGBA 的四个值,你可以根据需要使用这个数据(比如放大展示在界面上)。

Page({
  data: {
    color: ''
  },
  onCanvasTap(e) {
    const query = wx.createSelectorQuery()
    query.select('#myCanvas')
      .fields({
        node: true,
        size: true
      })
      .exec((res) => {
        const canvas = res[0].node
        const ctx = canvas.getContext('2d')
        const data = ctx.getImageData(e.detail.x, e.detail.y, 1, 1).data;
        const [red,gree,blue,alpha] = data;
      })
  },
});
Code language: JavaScript (javascript)
6ee6df690137fd06bc6166adb63caca1

在小程序中使用的 Canvas 2D API 绘制本地图片

小程序自 2.9.0 起,不再推荐使用其自己封装的 Canvas Context,而是更加推荐大家使用标准的 Canvas 2D API 来完成相关操作。因此,对于开发者来说,需要将过去的 Canvas API 调整为新的标准的 API 。

刚好我最近也实现了类似的功能,将这部分逻辑分享给大家。

页面布局

<view>
  <view><button bindtap="chooseImage">选择图片</button></view>
  <view><canvas id="myCanvas"  type="2d" style="background-color:gray;width: 100%;margin-top: 100rpx;"></canvas></view>
</view>
Code language: HTML, XML (xml)

页面逻辑

Page({
   chooseImage(){
    // 创建一个 Query
    const query = wx.createSelectorQuery()
    // 选中 Canvas 对象
    query.select('#myCanvas')
      .fields({
        node: true,
        size: true // 提取 Node 信息
      })
      .exec((res) => {

        // 获取到 Canvas Node
        const canvas = res[0].node

        // 获取到 Context
        const ctx = canvas.getContext('2d')
        wx.chooseMedia({
          count: 1,
          success: function (res) {
            // 提取图片的基本信息
            wx.getImageInfo({
              src: res.tempFiles[0].tempFilePath,
              success: imgInfo => {
                // 使用 canvas.createImage 来创建一个图片
                const img = canvas.createImage()
                img.src = imgInfo.path
                img.onload = () => {
                  // 将图片画在画布上
                  ctx.drawImage(img, 0, 0, imgInfo.width, imgInfo.height)
                }
              }
            })
          }
        })
      })
   }
})
Code language: JavaScript (javascript)

参考文档

a computer screen with a bunch of text on it

WordPress 出现 RedisException: OOM command not allowed when used memory > maxmemory 的报错怎么处理?

早晨起来,想登录博客,记录下自己的灵感,突然发现死活登录不上 WordPress 后台。

登录到 VPS 后台,发现没有出现我之前常出的问题 — 硬盘满了。于是再次回到网页端登录,仔细研究后发现,我的登录应该是成功的,但登录完成后,又重新跳转回来,根据这个情况,我猜测可能是登录态出现了问题。

于是尝试切换到 Safari 、Chrome 的无痕模式登录,依然没有解决问题。因此可以排除掉客户端的问题导致的。

找到问题

接下来就是查看服务端问题。登录到服务器上,找到 WordPress 的日志,查看最近的几条日志,突然在众多 Notice 当中,看到了一个 Exception:

RedisException: OOM command not allowed when used memory > 'maxmemory'
Code language: JavaScript (javascript)

看到这个报错,突然明白了问题在哪了。

我的 WordPress 使用 Redis 作为缓存,而我过去一直配置的缓存空间是 128M,看报错,显然是因为 Redis 使用的内存空间大于 128M,而我过去没有配置逐出机制(默认是 noeviction),导致直接 OOM 爆掉了。

而我的登录态也使用了 Redis,没办法在缓存当中塞入新的 Key,自然登录也就失败了。

解决问题

找到问题之后,下一步便是解决问题。

解决问题并不复杂,为 Redis 调整内存空间大小,并配置逐出机制,就可以解决这个问题。

maxmemory 221000000
maxmemory-policy allkeys-lru

将 Redis 逐出机制设置为 allkeys-lru ,并将内存设置为 200M 后,重启 Redis ,果然我的 WordPress 可以正常登录了。

参考

allkeys-lru 表示对移除最近使用最少的 (least recently used)Key。

更多算法可以参考 Key Evicution

df60e45f6bb619bb8596c1634d569c6e

天下文功,唯真诚不破

作为一个博客博主,由于不依靠博客来吃饭,且个人文笔着实一般,我对于博客的文章的要求不如很多人那么高。

但我同样也希望我的文章被更多的人看到 —— 如何才能在没有华丽的文笔和词藻的情况下,依然赢得读者的心 —— 唯真诚耳

我写文章不追求一定是要给别人传达最全面的信息(当然,也看具体的情况,如果是某个场景,我确实打算写成全面的「干货」,那就会集数月之功,只为产出一篇文章),而是更多的传递出我对于这个世界的认知、看法。

所以我从不追求文章一定是干货,而更关注的是这篇文章传递了什么样的信息,表达了什么样的含义。

干货,就留给专职写作的同志们吧!

person sitting on wooden dock over the lake during daytime

自我 PUA

在身边的不少人眼中,我算一个很厉害的人 — 年纪轻轻就能获得超出自己这个年龄大多数人能做到的水平。

不过我自己知道,其实我并不是一个特别优秀的人 —— 我有着普通的出身,过着普通的生活,上着普通的学校,并没有什么不普通。即使是别人看来不错的成就,在我看来也不过是普通的成就 —— 那些聪明人轻轻松松就能获得的。

而如果问 —— 那为什么你能做到,别人做不到呢?我想,这可能源自于我很擅长自我 PUA。我会不断的给自己加上更多的压力,在压力的逼迫下不断的去调整自己的极限。

通过自我 PUA ,我的确一直在逼着自己变得更好 —— 不一定是比天之骄子做的好很多,但确实超越了曾经的自己。

不过,我也知道,其实这种状态不可持续,也不是真正的强大 —— 我总会有一天把所有的压力累积到爆炸,然后彻底重来。我也并不能做到和那些特别优秀的人一样,能够游刃有余的处理所有事情。

不过,好在我认识到了这一点,所以,放过自己,对于做不到的事情,坦然的接受我做不到。

shallow focus photography of red and white for hire signage

裁员不稀奇

最近裁员消息到处都是,看群里一个人提到 —— 公司裁员以后,发现业务正常运转。让我想起了一个梗 —— 不敢居家办公,怕公司发现离开了你业务照常运转。

对于很多小公司来说,由于公司规模小,所以每个岗位的员工都不可或缺。但对于存在裁员新闻当中的大公司 —— 员工体系的设计便是希望你们可以每个人都能被替换掉,每个人都是标准化的螺丝钉。因此,在这样的公司当中,裁员业务正常运转丝毫不稀奇。

不过,裁员也并非完全没有坏处 —— 裁员可能不会让业务停止运转,但大概率会降低业务运转的效率—— 毕竟过去两个人能做的事情,现在只有一个人做了,自然会按照优先级调整,优先安排高优事项。低优事情能做就做,不能做就等等再做。

希望我们的经济可以扛过这一波疫情的影响。

哎,兴,百姓苦;亡,百姓苦。天下皆苦。

428be18119fcba733c3990cf8afde346

保险很重要

人生走过三分之一,身边开始出现了水滴筹,再一次的提醒我保险的重要性。

d2b5ca33bd970f64a6301fa75ae2eb22

作为一个关注保险,且也在交保险的人,自然是能认可保险的价值 —— 付一笔小钱,用来对抗后续可能的大钱。不过,合理的保险配置确实是大部分人都没有关注过的。

大部分人应该只有医保,其中一部分的人购买了补充的商业保险。最近几年,各地城市开始推广城市惠民保开始让大家的城市配置率变得更高一些。

但我相信,配置保险依然是一个小众的行为 —— 当然,在我所在的互联网圈子里可能是个偏大众的选择。但对于更多的普通民众来说,这是一个小众的选择。

person using black smartphone with gray and pink case

灵感 – 播客地推工具

即刻有 JikePage 这样的能力,实现了诸如 Linktree 这样的产品能力。但这个部分其实还有可以更新迭代的方向,比如针对播客场景,提供更加适合地推 —— 破圈的能力。

在我的想象中,这个工具应该具备以下能力

  1. 以播客为维度创建独立的页面;
  2. 可以直接导入 Feeds 地址,选择展示哪些节目;
  3. 对于导入的内容, 默认可以展示 Shownotes ,但允许主播调整;
  4. 每一个节目有一个自己独立的 ID, 可以用于分享链接;
  5. 每一个节目都可以直接在浏览器里收听(不同于小宇宙 Web 版的点是可以选择其中的部分内容,用作破圈,可以选择基于 HTTP 的 Ranges 能力来实现)
  6. 能够生成二维码、分享卡片;
  7. 能够从节目跳转到播客介绍页面;
  8. (如果是小宇宙做的话)可以选择最热评论中的某几个展示在页面上;
  9. 能够跳转到具体的播客订阅页面;
  10. 支持开启播客互推(即如果开启互推,你的节目也会出现在别人的播客下方,作为猜你喜欢的实现;相应的,你的节目下方也会出现别人的播客)。