Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
background-color 屬性為元素定義了背景顏色:
<html> <body style="background-color:yellow"> <h2 style="background-color:red">This is a heading</h2> <p style="background-color:green">This is a paragraph.</p> </body> </html>
HTML樣式:好看的顏色字體是有方法的
23 年的 CSS 新特性中,有一個(gè)非常重要的功能更新 -- 相對顏色。
簡單而言,相對顏色的功能,讓我們在 CSS 中,對顏色有了更為強(qiáng)大的掌控能力。
其核心功能就是,讓我們能夠基于一個(gè)現(xiàn)有顏色 A,通過一定的轉(zhuǎn)換規(guī)則,快速生成我們想要的顏色 B。
其功能能夠涵蓋:
當(dāng)然,今天我們不會(huì)一個(gè)一個(gè)去過這些功能,更多的時(shí)候,我們只需要知道我們能夠?qū)崿F(xiàn)這些功能。
本文,我們將從實(shí)際實(shí)用角度出發(fā),基于實(shí)際的案例,看看 CSS 相對顏色,能夠如何解決我們的一些實(shí)際問題。
首先,我們通過一張圖,一個(gè)案例,快速入門 CSS 相對顏色語法:
相對顏色語法的目標(biāo)是允許從另一種顏色派生顏色。
上圖顯示了將原始顏色 green 轉(zhuǎn)換為新顏色的顏色空間后,該顏色會(huì)轉(zhuǎn)換為以 r、g、b 和 alpha 變量表示的各個(gè)數(shù)字,這些數(shù)字隨后會(huì)直接用作新的 rgb() 顏色的值。
舉個(gè)例子:
<p> CSS Relative Color </p>
p {
color: rgb(255, 0, 0);
}
實(shí)現(xiàn)一個(gè) color 為紅色(rgb 值為 rgb(255, 0, 0))的字體:
基于上面的相對顏色語法,我如何通過一個(gè)紅色生成綠色文字呢?示意如下:
p {
--color: rgb(255, 0, 0);
color: rgb(from var(--color) calc(r - 255) calc(g + 255) b); /* result = rgb(0, 255, 0) */
}
效果如下,我們就得到綠色字體:
解釋一下:
通過這個(gè) DEMO,我們把幾個(gè)核心基礎(chǔ)語法點(diǎn)學(xué)習(xí)一下:
from 關(guān)鍵字,它是相對顏色的核心。它表示會(huì)將 from 關(guān)鍵字后的顏色定義轉(zhuǎn)換為相對顏色!在 from 關(guān)鍵字后面,CSS 會(huì)期待一種顏色,即能夠啟發(fā)生成另一種顏色。
第二個(gè)關(guān)鍵點(diǎn),from 后面通常會(huì)接一個(gè)顏色值,這個(gè)顏色值可以是任意顏色表示法,或者是一個(gè) CSS 變量,下面的寫法都是合法的:
p {
color: rgba(from #ff0000) r g b);
color: rgb(from rgb(255, 0, 0) r g b);
color: rgb(from hsl(0deg, 100%, 50%) r g b);
color: rgb(from var(--hotpink) r g b);
}
另外一個(gè)非常重要的基礎(chǔ)概念就是,我們可以對 (from color r g b) 后的轉(zhuǎn)換變量 r g b 使用 calc() 或其他 CSS 函數(shù)。
就是我們上面的例子:
p {
--color: rgb(255, 0, 0);
color: rgb(from var(--color) calc(r - 255) calc(g + 255) b); /* result = rgb(0, 255, 0) */
}
相對顏色的基礎(chǔ)的使用規(guī)則就是這樣,它不僅支持 rgb 顏色表示法,它支持所有的顏色表示法:
通常頁面上的按鈕,都會(huì)有 hover/active 的顏色變化,以增強(qiáng)與用戶的交互。
像是這樣:
最常見的寫法,就是我們需要在 Normal 狀態(tài)、Hover 狀態(tài)、Active 狀態(tài)下寫 3 種顏色:
p {
color: #ffcc00;
transition: .3s all;
}
/* Hover 偽類下為 B 顏色 */
p:hover {
color: #ffd21f;
}
/** Active 偽類下為 C 顏色 **/
p:active {
color: #ab8a05;
}
在之前,我們介紹過一種利用濾鏡 filter: contrast() 或者 filter: brightness() 的統(tǒng)一解決方案,無需寫多個(gè)顏色值,可以根據(jù) Normal 狀態(tài)下的色值,通過濾鏡統(tǒng)一實(shí)現(xiàn)更亮、或者更暗的偽類顏色。
在今天,我們也可以利用 CSS 相對顏色來做這個(gè)事情:
div {
--bg: #fc0;
background: var(--bg);
transition: .3s all;
}
div:hover {
background: hsl(from var(--bg) h s calc(l * 1.2));
}
div:active {
background: hsl(from var(--bg) h s calc(l * 0.8));
}
我們通過 hsl 色相、飽和度、亮度顏色表示法表示顏色。實(shí)現(xiàn):
在實(shí)際業(yè)務(wù)中,這是一個(gè)非常有用的用法。
相對顏色,還有一個(gè)非常有意思的場景 -- 讓文字顏色能夠自適應(yīng)背景顏色進(jìn)行展示。
有這么一種場景,有的時(shí)候,無法確定文案的背景顏色的最終表現(xiàn)值(因?yàn)楸尘邦伾闹悼赡苁呛笈_配置,通過接口傳給前端),但是,我們又需要能夠讓文字在任何背景顏色下都正常展現(xiàn)(譬如當(dāng)?shù)咨珵楹谏珪r(shí)文字應(yīng)該是白色,當(dāng)背景為白色時(shí),文字應(yīng)該為黑色)。
像是這樣:
在不確定背景顏色的情況下,無論什么情況,文字顏色都能夠適配背景的顏色。
在之前,純 CSS 沒有特別好的方案,可以利用 mix-blend-mode: difference 進(jìn)行一定程度的適配:
div {
// 不確定的背景色
}
p {
color: #fff;
mix-blend-mode: difference;
}
實(shí)操過這個(gè)方案的同學(xué)都會(huì)知道,在一定情況下,前景文字顏色還是會(huì)有一點(diǎn)瑕疵。并且,混合模式這個(gè)方案最大的問題是會(huì)影響清晰度。
有了 CSS 相對顏色后,我們有了更多的純 CSS 方案。
我們可以利用相對顏色的能力,基于背景色顏色進(jìn)行反轉(zhuǎn),賦值給 color。
一種方法是將顏色轉(zhuǎn)換為 RGB,然后從 1 中減去每個(gè)通道的值。
代碼非常簡單:
p {
/** 任意背景色 **/
--bg: #ffcc00;
background: var(--bg);
color: rgb(from var(--bg) calc(1 - r) calc(1 - g) calc(1 - b)); /** 基于背景反轉(zhuǎn)顏色 **/
}
用 1 去減,而不是用 255 去,是因?yàn)榇丝蹋瑫?huì)將 rgb() 表示法中的 0~255 映射到 0~1。
效果如下:
配個(gè)動(dòng)圖,我們利用背景色的反色當(dāng) Color 顏色,適配所有背景情況:
完整的 DEMO 和代碼,你可以戳這里:CodePen Demo -- CSS Relatvie Color Adapt BG
當(dāng)然,這個(gè)方案還有兩個(gè)問題:
為了解決這兩個(gè)問題,CSS 顏色規(guī)范在 CSS Color Module Level 6 又推出了一個(gè)新的規(guī)范 -- color-contrast()。
color-contrast() 函數(shù)標(biāo)記接收一個(gè) color 值,并將其與其他的 color 值比較,從列表中選擇最高對比度的顏色。
利用這個(gè) CSS 顏色函數(shù),可以完美的解決上述的問題。
我們只需要提供 #fff 白色和 #000 黑色兩種可選顏色,將這兩種顏色和提供的背景色進(jìn)行比較,系統(tǒng)會(huì)自動(dòng)選取對比度更高的顏色。
改造一下,上面的代碼,它就變成了:
p {
/** 任意背景色 **/
--bg: #ffcc00;
background: var(--bg);
color: color-contrast(var(--bg) vs #fff, #000); /** 基于背景色,自動(dòng)選擇對比度更高的顏色 **/
}
這樣,上面的 DEMO 最終效果就變成了:
此方案的優(yōu)勢在于:
當(dāng)然,唯一限制這個(gè)方案的最大問題在于,當(dāng)前,color-contrast 還只是一個(gè)實(shí)驗(yàn)室功能,未大規(guī)模被兼容。
到今天,我們可以利用 CSS 提供的各類顏色函數(shù),對顏色有了更為強(qiáng)大的掌控力。
很多交互效果,不借助 JavaScript 的運(yùn)算,也能計(jì)算出我們想要的最終顏色值。本文簡單的借助:
兩個(gè)案例,介紹了 CSS 相對顏色的功能。
原文鏈接:https://juejin.cn/post/7321410822789742618
在學(xué)習(xí)和工作中,我們經(jīng)常需要使用日志來記錄程序的運(yùn)行狀態(tài)和調(diào)試信息。而為了更好地區(qū)分不同的日志等級,我們可以使用不同的顏色來呈現(xiàn),使其更加醒目和易于閱讀。
在下圖運(yùn)行結(jié)果中,我們使用了 colorlog 庫來實(shí)現(xiàn)彩色日志輸出。通過定義不同日志等級對應(yīng)的顏色,我們可以在控制臺中以彩色的方式顯示日志信息。例如,DEBUG 級別的日志使用白色,INFO 級別的日志使用綠色,WARNING 級別的日志使用黃色,ERROR 級別的日志使用紅色,CRITICAL 級別的日志使用藍(lán)色。
但是在查看日志文件時(shí),我們會(huì)發(fā)現(xiàn)日志信息是系統(tǒng)默認(rèn)的字體顏色,并且前后多了一些特殊符號,例如 [32m 等。這是因?yàn)樵诳刂婆_中使用的是 ANSI 轉(zhuǎn)義序列來實(shí)現(xiàn)彩色文本效果,而這些特殊符號是 ANSI 轉(zhuǎn)義序列的一部分。如下圖所示:
現(xiàn)在有一個(gè)需求,在前端頁面直接查看日志內(nèi)容并還原彩色文本效果,因此,我們將進(jìn)行以下內(nèi)容講解:
本文代碼點(diǎn)擊此處跳轉(zhuǎn),往期系列文章請?jiān)L問博主的 項(xiàng)目實(shí)戰(zhàn)專欄,博文中的所有代碼全部收集在博主的 GitHub 倉庫中;
ANSI 轉(zhuǎn)義序列是美國國家標(biāo)準(zhǔn)化組織(American National Standards Institute,ANSI)制定的標(biāo)準(zhǔn),是一種用于控制文本終端顯示的特殊字符序列。它們以 3[ 開頭,以字母和數(shù)字組合的形式表示不同的控制功能。
ANSI 轉(zhuǎn)義序列可以用于控制文本的顏色、背景色、文本樣式(如粗體、斜體等)、光標(biāo)位置、清屏等操作。通過在輸出文本中插入適當(dāng)?shù)?ANSI 轉(zhuǎn)義序列,可以實(shí)現(xiàn)豐富的終端顯示效果。
以下是一些常用的 ANSI 轉(zhuǎn)義序列示例:
需要注意的是,ANSI 轉(zhuǎn)義序列在不同的終端和操作系統(tǒng)上的支持程度可能會(huì)有所不同。在某些終端中,可能無法正確解釋和顯示 ANSI 轉(zhuǎn)義序列。
我們以 3[31m 和 3[42m 為例,輸出一個(gè)綠底紅字的句子 Hello World! --sidiot.,代碼如下所示:
log.debug("3[42m3[31mHello World! --sidiot.3[0m3[0m")
運(yùn)行結(jié)果:
這里的話,我們使用 Python 的 http.server 模塊來啟動(dòng)一個(gè)簡單的 HTTP 服務(wù)器。
比較快捷的方式就是在日志文件夾中打開終端,輸入 python -m http.server 8888 即可,運(yùn)行結(jié)果如下所示:
不過這種方式相對來說還是不太安全的,因此我們可以通過設(shè)置白名單的方式,來規(guī)避一些潛在的安全隱患,代碼如下所示:
import http.server
import socketserver
class HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def check_client_address(self):
# 設(shè)置白名單,只允許特定的IP地址或主機(jī)訪問
whitelist = ['127.0.0.1', 'localhost']
client_address = self.client_address[0]
if client_address not in whitelist:
self.send_response(403)
self.end_headers()
self.wfile.write(b'Forbidden. Please contact sidiot.')
return False
return True
def do_GET(self):
if not self.check_client_address():
return
super().do_GET()
with socketserver.TCPServer(('0.0.0.0', 8888), HTTPRequestHandler) as httpd:
httpd.serve_forever()
目前本機(jī)的 IP 為 192.168.124.23,當(dāng)我們以 127.0.0.1 來訪問 8888 服務(wù)端口時(shí),訪問是成功的,但是當(dāng)我們用 192.168.124.23 來訪問服務(wù)端口時(shí),訪問是失敗的。
運(yùn)行結(jié)果:
現(xiàn)在我們點(diǎn)擊文件,它會(huì)直接通過瀏覽器直接下載,但是我們需要的是在網(wǎng)頁上能夠直接閱覽文件中的內(nèi)容,因此我們可以從 do_GET() 下手。
我們可以設(shè)計(jì)一個(gè)根據(jù)傳入的文件名參數(shù),讀取本地文件并作為響應(yīng)結(jié)果進(jìn)行返回的方法,然后根據(jù)一定的規(guī)則進(jìn)行觸發(fā),代碼如下所示:
def read_file(self):
try:
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write(open(self.path[6:], 'rb').read())
except FileNotFoundError:
self.send_response(404)
self.end_headers()
self.wfile.write(b'File not found!')
def do_GET(self):
if self.check_client_address():
if self.path.startswith("/?log="):
self.read_file()
else:
super().do_GET()
上述代碼通過檢查請求的資源路徑來處理 GET 請求。如果請求的資源路徑前綴是 /?log=,且是當(dāng)前目錄下存在的日志文件,它會(huì)讀取文件并將其內(nèi)容作為響應(yīng)發(fā)送。否則,它會(huì)使用基類的默認(rèn)行為處理普通的 GET 請求。
運(yùn)行結(jié)果:
至此,我們已經(jīng)實(shí)現(xiàn)了前端頁面直接查看日志內(nèi)容的功能。
原理分析
當(dāng)我們想要在前端頁面展示 ANSI 字體的彩色效果時(shí),我們只需要簡單地將 ANSI 轉(zhuǎn)義序列轉(zhuǎn)換成相應(yīng)的 HTML 代碼就可以實(shí)現(xiàn)了。這個(gè)轉(zhuǎn)換過程實(shí)際上可以通過編寫一個(gè) Python 函數(shù)來實(shí)現(xiàn),該函數(shù)可以接受包含 ANSI 控制碼的字符串作為輸入,并將其轉(zhuǎn)換為帶有相應(yīng)樣式的 HTML 代碼輸出,代碼如下所示:
def convert_ansi_to_html(ansi_text):
ansi_to_html = {
'\x1b[31m': '<span style="color: red;">',
'\x1b[42m': '<span style="background-color: green;">',
...,
}
html_text = re.sub(r'\x1b[[0-9;]*m', lambda match: ansi_to_html.get(match.group(0), ''), ansi_text)
return html_text
if __name__ == '__main__':
ansi = "\033[42m\033[31mHello World! --sidiot.\033[0m\033[0m"
print(ansi)
html = convert_ansi_to_html(ansi)
print(f"convert content: {html}")
需要注意的是,在 ANSI 轉(zhuǎn)義序列中,\x1b 和 3 都代 表ASCII 碼中的 Escape 字符,用于開始一個(gè)轉(zhuǎn)義序列。
運(yùn)行結(jié)果:
使用 ansiconv 轉(zhuǎn)換
接下來,我們借助已有的庫函數(shù) ansiconv 進(jìn)行 ANSI 的轉(zhuǎn)換。
通過 pip 進(jìn)行安裝:
pip install ansiconv
根據(jù) ansiconv 的官方文檔使用其中的三個(gè)方法 to_plain(),to_html() 和 base_css() 來實(shí)現(xiàn)在前端頁面展示 ANSI 字體的彩色效果,代碼如下所示:
import ansiconv
ansi = "\033[42m\033[31mHello World! --sidiot.\033[0m\033[0m"
print(f"Ansi: {ansi}")
plain = ansiconv.to_plain(ansi)
html = ansiconv.to_html(ansi)
print(f"Convert Plain: {plain}")
print(f"Convert HTML: {html}")
在 base_css() 中會(huì)有相關(guān)的 CSS 映射表,如下所示:
css_rule('.ansi31', color="#FF0000"),
css_rule('.ansi42', background_color="#00FF00"),
運(yùn)行結(jié)果:
研究 ansiconv 源碼
我們將通過研究 ansiconv 的源碼,以便深入了解它是如何將 ANSI 轉(zhuǎn)換成純文本或 HTML 代碼的工作原理。
to_plain() 的源碼如下所示:
上述代碼使用正則表達(dá)式匹配字符串中的 ANSI 轉(zhuǎn)義序列,并將其替換為空字符串,從而得到不包含轉(zhuǎn)義序列的純文本。
正則表達(dá)式的含義如下:
我們通過 re.findall() 方法來獲取所有匹配的結(jié)果,這樣夠清晰地捕獲所有符合條件的匹配項(xiàng),從而更好地理解 ansiconv 是如何進(jìn)行 ANSI 到純文本的轉(zhuǎn)換,代碼如下所示:
ansi = "\033[42m\033[31mHello World! --sidiot.\033[0m\033[0m"
print(re.findall(r'\x1B[[0-9;]*[ABCDEFGHJKSTfmnsulh]', ansi))
運(yùn)行結(jié)果:
to_html() 的源碼如下所示:
上述代碼將 ANSI 字符串分割成塊,并對每個(gè)塊調(diào)用 _block_to_html() 函數(shù)進(jìn)行解析和轉(zhuǎn)換,同時(shí)還處理了 ANSI 命令 "A",模擬向上移動(dòng)光標(biāo)的行為。如果 replace_newline 為 True,則 HTML 字符串中的換行符 \n 將替換為 <br />\n 以保留 HTML 輸出中的換行符。
其中 _block_to_html() 的源碼如下所示:
上述代碼使用正則表達(dá)式匹配 ANSI 代碼,并根據(jù)匹配結(jié)果生成對應(yīng)的 HTML 代碼。
正則表達(dá)式的含義:
我們可以通過運(yùn)行源碼里的部分代碼來幫助理解,代碼如下所示:
text = ("\x1B[0;32;45msidiot\n"
"\033[42m\033[31mHello World! --sidiot.\033[0m\033[0m")
print(text)
blocks = text.split('\x1B')
print(blocks)
for block in blocks:
match = re.match(r'^[(?P<code>\d+(?:;\d+)*)?(?P<command>[Am])', block)
if match is not None:
print("\nmatch:", match, ", code:", match.group('code'), ", command:", match.group('command'))
運(yùn)行結(jié)果:
實(shí)際應(yīng)用
通過深入理解 ANSI 轉(zhuǎn)換思路和 ansiconv 源碼,我們可以為之前的 http.server 服務(wù)帶來全新的優(yōu)化。
首先,將原先的 read_file() 方法進(jìn)行優(yōu)化,代碼如下所示:
def read_file(self, content_type, file_io):
try:
self.send_response(200)
self.send_header("Content-Type", f"{content_type}; charset=utf-8")
self.end_headers()
self.wfile.write(file_io)
except FileNotFoundError:
self.send_response(404)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write(b'File not found!')
上述代碼通過接收 content_type 和 file_io 兩個(gè)參數(shù),實(shí)現(xiàn)將自定義內(nèi)容作為響應(yīng)返回給客戶端。
然后修改請求路徑,使其能夠返回純文本和 HTML 兩種不同類型的內(nèi)容,代碼如下所示:
def do_GET(self):
if self.check_client_address():
if self.path.startswith("/?plain="):
file = open(self.path[8:], 'rb').read()
plain = ansiconv.to_plain(file.decode('UTF-8'))
self.read_file("text/plain", plain.encode())
elif self.path.startswith("/?html="):
file = open(self.path[7:], 'rb').read()
conv = ansiconv.to_html(file.decode('UTF-8'))
css = ansiconv.base_css()
html = """
<html>
<head><style>{0}</style></head>
<body>
<pre class="ansi_fore ansi_back">{1}</pre>
</body>
</html>
""".format(css, conv)
print(html)
self.read_file("text/html", html.encode())
else:
super().do_GET()
這里要注意的是,需要設(shè)置 CSS 樣式,不然 class 類是無法進(jìn)行渲染的。
純文本運(yùn)行結(jié)果:
HTML 運(yùn)行結(jié)果:
在本文中,我們探討了如何實(shí)現(xiàn)將 ANSI 字體在前端頁面進(jìn)行彩色展示的方法。在前端頁面中直接顯示 ANSI 轉(zhuǎn)義序列是不起作用的,因?yàn)闉g覽器不會(huì)解析和處理這些轉(zhuǎn)義序列。
為了在前端頁面實(shí)現(xiàn)彩色展示,我們介紹了一種方法,即將 ANSI 轉(zhuǎn)義序列轉(zhuǎn)換為對應(yīng)的 HTML 代碼。通過解析 ANSI 轉(zhuǎn)義序列并將其轉(zhuǎn)換為適當(dāng)?shù)?HTML 標(biāo)簽和樣式,我們可以在前端頁面上還原彩色文本的效果。
在本文中,我們使用了 Python 中的 ansiconv 庫來實(shí)現(xiàn) ANSI 轉(zhuǎn)換。該庫提供了 to_plain 和 to_html 兩個(gè)方法,分別用于將 ANSI 轉(zhuǎn)義序列轉(zhuǎn)換為純文本和 HTML 代碼。我們還展示了如何使用這些方法來轉(zhuǎn)換 ANSI 字符串,并在前端頁面上顯示轉(zhuǎn)換后的結(jié)果。
通過本文的介紹,讀者可以了解到如何在前端頁面實(shí)現(xiàn)彩色文本的展示,從而提升用戶體驗(yàn)和可讀性。無論是在日志查看器、終端模擬器還是其他需要展示彩色文本的應(yīng)用中,這種技術(shù)都能發(fā)揮重要作用。
以上就是 從終端到瀏覽器:實(shí)現(xiàn) ANSI 字體在前端頁面的彩色展示 的所有內(nèi)容了,希望本篇博文對大家有所幫助!歡迎大家持續(xù)關(guān)注我的博客,一起分享學(xué)習(xí)和成長的樂趣!?
作者:sidiot
鏈接:https://juejin.cn/post/7381820436274184202
*請認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。