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
看到這個標題,相信大多數(shù)人的第一反應(yīng)是:真的有人用 40 億條 if 語句,只為判斷一個數(shù)字是奇數(shù)還是偶數(shù)?的確有,這個開發(fā)者名為 Andreas Karlsson,他還把整個過程都整理成文了。
或許由于這“40 億條 if 語句”聽起來實在震撼,Andreas Karlsson 分享的這篇文章在 Hacker News 上很快引起了極大的關(guān)注和討論,而他在文中也直白表示:其實這個想法,最初源于一個充滿惡評的短視頻。
以下為譯文:
大于 11 的數(shù)字,沒有輸出結(jié)果
我最近在火車上刷手機時,偶然發(fā)現(xiàn)了上面這個截圖:“寫了一個程序,來判斷一個數(shù)字是偶數(shù)還是奇數(shù)。”點開評論區(qū),果然是一連串的惡意評論,多數(shù)都在嘲笑這位新手程序員的稚嫩和無知,竟企圖以這種方式解決計算機科學(xué)中的經(jīng)典問題“取模運算”。
可看過截圖中的代碼和網(wǎng)友評論后,我莫名生出了一些不同的想法:現(xiàn)在,AI 正在分分鐘取代程序員、搶走他們的飯碗,并徹底改變了我們對代碼的思考方式,或許我們應(yīng)該更加開放地接受這個行業(yè)新生代的思想?
其實仔細想來,上述代碼是時間和空間的一種完美權(quán)衡:你在付出自己時間的同時,也換來了計算機的內(nèi)存和時間——這難道不是一個神奇的算法嗎?
于是,我開始探索這種只使用比較來判斷一個數(shù)字是奇數(shù)還是偶數(shù)的想法,看看它在實際情況中的效果到底如何。由于我是一位高性能代碼的忠實擁護者,因此我決定用 C 語言來實現(xiàn)這個想法。
然后,我就開始編碼了:
/* Copyright 2023. All unauthorized distribution of this source code
will be persecuted to the fullest extent of the law*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
uint8_t number = atoi(argv[1]); // No problems here
if (number == 0)
printf("even\n");
if (number == 1)
printf("odd\n");
if (number == 2)
printf("even\n");
if (number == 3)
printf("odd\n");
if (number == 4)
printf("even\n");
if (number == 5)
printf("odd\n");
if (number == 6)
printf("even\n");
if (number == 7)
printf("odd\n");
if (number == 8)
printf("even\n");
if (number == 9)
printf("odd\n");
if (number == 10)
printf("even\n");
}
接下來,我們要編譯這段代碼,使用 /Od 禁用優(yōu)化,確保煩人的編譯器不會干擾我們的算法。編譯完成后,我們就可以對程序進行快速測試,看看結(jié)果如何:
PS > cl.exe /Od program.c
PS > .\program.exe 0
even
PS > .\program.exe 4
even
PS > .\program.exe 3
odd
PS > .\program.exe 7
odd
結(jié)果顯示:0、4 是偶數(shù),3、7 是奇數(shù)。這么看來,程序似乎運行得挺好,但在進一步測試后,我發(fā)現(xiàn)了一些問題:
PS > .\program.exe 50
PS > .\program.exe 11
PS > .\program.exe 99
大于 11 的數(shù)字沒有輸出,看來這個程序只對 11 以下的數(shù)字有效!回到原始代碼中,可以發(fā)現(xiàn)問題出在最后一個 if 語句之后:我們需要更多的 if 語句!
向 32 位(32-bit)數(shù)擴展
這件事進行到這里,就需要我在時間和內(nèi)存之間做出權(quán)衡了。考慮到我的壽命有限,我決定用另一種編程語言對 if 語句進行元編程。為了彌補這種“作弊”行為,我決定用“地球上速度最慢”的語言 Python。
print("/* Copyright 2023. All unauthorized distribution of this source code")
print(" will be persecuted to the fullest extent of the law*/")
print("#include <stdio.h>")
print("#include <stdint.h>")
print("#include <stdlib.h>")
print("int main(int argc, char* argv[])")
print("{")
print(" uint8_t number = atoi(argv[1]); // No problems here")
for i in range(2**8):
print(" if (number == "+str(i)+")")
if i % 2 == 0:
print(" printf(\"even\n\");")
else:
print(" printf(\"odd\n\");")
print("}")
好了!現(xiàn)在我們可以生成一個程序,解決所有 8 位(8-bit)整數(shù)的奇偶問題!
PS > python programmer.py > program.c
PS > cl.exe /Od program.c
PS > .\program.exe 99
odd
PS > .\program.exe 50
even
PS > .\program.exe 240
even
PS > .\program.exe 241
odd
看看,這個效果簡直完美!現(xiàn)在,讓我們把它放大到 16 位(16-bit)!
print(" uint16_t number = atoi(argv[1]); // No problems here")
…
for i in range(2**16):
這樣就得到了一個約 13 萬行、超長且漂亮的 c 文件。回顧了一下我多年工作所做的一些代碼庫,這其實不算什么。話不多說,開始編譯!
PS > python programmer.py > program.c
PS > cl.exe /Od program.c
PS > .\program.exe 21000
even
PS > .\program.exe 3475
odd
PS > .\program.exe 3
odd
PS > .\program.exe 65001
odd
PS > .\program.exe 65532
even
太棒了,我們的算法似乎能夠處理大量數(shù)據(jù)!可執(zhí)行文件大約只有 2 MB,但這與我擁有高達 31.8 GB 內(nèi)存的強大游戲設(shè)備相比,簡直不值一提。
但眾所周知,32 位(32-bit)才是計算機領(lǐng)域的終極目標,也是我們解決所有實際工程和科學(xué)問題所需的最終位寬。畢竟,在 IPv4 因所謂的 "地址耗盡 "而被認為過時 60 年后,它如今仍然很強大。所以,讓我們來看看最終的規(guī)模:32 位的數(shù)字是 16 位的 65536 倍,這會有什么問題嗎?
print(" uint32_t number = atoi(argv[1]); // No problems here")
…
for i in range(2**32):
于是,我讓強大的 Python 開始它的工作。48 小時后,我喝了一杯咖啡,然后回來檢查程序,就得到了一個美麗的 c 文件,大小接近 330 GB!我?guī)缀蹩梢钥隙ǎ@是歷史上最大的 c 文件之一。當(dāng)我輸入下一條命令時,我的手指都在顫抖,我猜 MSVC 肯定從未遇到如此強大的源代碼。
在我那臺可憐而強大的電腦頁面文件中遭受半小時的折磨后,輸出如下:
PS > cl /Od program.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.32.31329 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
program.c
program.c(134397076): warning C4049: compiler limit: terminating line number emission
program.c(134397076): note: Compiler limit for line number is 16777215
program.c(41133672): fatal error C1060: compiler is out of heap space
太令人失望了!不僅編譯器讓我失望,在研究 Windows 可移植可執(zhí)行文件格式(.exe)的限制時,我發(fā)現(xiàn)它無法處理超過 4GB 的文件!由于需要將 40 多億次比較語句編碼到可執(zhí)行文件中,這對于實現(xiàn)我們的算法是一個主要障礙。即使每次比較時使用的字節(jié)數(shù)少于一個,對我來說工作量也太大了。
不過,糟糕的編譯器和文件格式不應(yīng)該阻止我們實現(xiàn)夢想。畢竟,編譯器所做的只是將一些花哨的機器代碼寫入文件,而文件格式只是一些結(jié)構(gòu),告訴操作系統(tǒng)如何將二進制代碼放入內(nèi)存——其實,我們自己就能做到。
解決最后一個問題,程序性能很不錯
讓我們先用 x86-64 匯編語言編寫一個 IsEven 函數(shù),因為這是我 Intel 處理器驅(qū)動的本地語言,它看起來是這樣的:
; Argument is stored in ECX, return value in EAX
XOR EAX, EAX ; Set eax to zero (return value for odd number)
CMP ECX, 0h ; Compare arg to 0
JNE 3h ; Skip next two instructions if it wasn't equal
INC EAX ; It was even, set even return value (1)
RET ; Return
CMP ECX, 1h ; Compare arg to 1
JNE 2 ; Skip next instruction if not equal
RET ; Odd return value already in EAX, just RET
; add the next 2...2^32-1 comparisons here
RET ; Fallback return
這并不是真正正確的匯編代碼,但這不重要,因為我們要手動將其編譯成機器代碼。
你問我是怎么做到的?我上網(wǎng)查閱了x86(-64) 體系結(jié)構(gòu)手冊,還利用我早年編寫仿真器和黑客經(jīng)驗,找出了每條指令的正確操作碼和格式……開個玩笑,這是不可能的。實際上,我是直接問 ChatGPT 每條指令的正確操作碼是什么,幸運的是,它也沒有產(chǎn)生 x86-64 的任何新擴展。
所以現(xiàn)在我們只需編寫一個“編譯器”來輸出這段代碼。請注意,我們將直接使用從 AI 獲取的指令操作碼,下面是用 Python 編寫的代碼:
import struct
with open('isEven.bin', 'wb') as file:
file.write(b"\x31\xC0") # XOR EAX, EAX
for i in range(2**32):
ib = struct.pack("<I", i) # Encode i as 32 bit little endian integer
file.write(b"\x81\xF9" + ib) # CMP ECX, i
if i%2 == 0:
file.write(b"\x75\x03") # JNE +3
file.write(b"\xFF\xC0") # INC EAX
file.write(b"\xC3") # RET
else:
file.write(b"\x75\x01") # JNE +1
file.write(b"\xC3") # RET
file.write(b"\xC3") # Fallback RET
雖然我們在一定程度上偏離了開頭 TikTok 帖子的最初構(gòu)想,但本質(zhì)并沒有改變:我們創(chuàng)建了一個非常長的 if 語句列表,用于確定某個數(shù)字是奇數(shù)還是偶數(shù),并忽略了任何有助于簡化問題的算術(shù)運算。
運行這個程序后,我們就得到了一個 40GB 的文件,其中包含了確定 32 位數(shù)字是偶數(shù)還是奇數(shù)所需的全部 42 億次比較!現(xiàn)在,我們只需編寫能夠加載和使用這些指令的主程序。為了提高性能(這一點非常重要),我決定將文件映射到地址空間,而非一次性讀取全部文件。這樣,我們就可以假裝整個文件已經(jīng)在內(nèi)存中,讓可憐的操作系統(tǒng)來處理將一個 40GB 的 Blob 裝入虛擬內(nèi)存的問題。用 READ 和 EXECUTE 權(quán)限映射文件后,我們就可以使用函數(shù)指針調(diào)用代碼了,代碼如下:
#include <stdio.h>
#include <Windows.h>
#include <stdint.h>
int main(int argc, char* argv[])
{
uint32_t number = atoi(argv[1]); // No problems here
// Open code file
HANDLE binFile = CreateFileA(
"isEven.bin",
GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ,
,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
);
// Get 64 bit size of file
LARGE_INTEGER codeSize;
GetFileSizeEx(binFile, &codeSize);
// Create memory map of the file
HANDLE mapping = CreateFileMapping(
binFile,
,
PAGE_EXECUTE_READ,
0,
0,
);
// Get a pointer to the code
LPVOID code = MapViewOfFile(
mapping,FILE_MAP_EXECUTE | FILE_MAP_READ,
0,
0,
codeSize.QuadPart);
// Create a function that points to the code
int (*isEven)(int) = (int(*)(int))code;
if (isEven(number))
printf("even\n");
else
printf("odd\n");
CloseHandle(binFile);
}
就是這樣!現(xiàn)在我們已經(jīng)具備了判斷任何 32 位(32-bit)數(shù)字是奇是偶的所有功能,讓我們試一試:
PS >.\program.exe 300
even
PS >.\program.exe 0
even
PS >.\program.exe 1000000
even
PS >.\program.exe 100000007
odd
PS >.\program.exe 400000000
even
PS >.\program.exe 400000001
odd
PS >.\program.exe 400000006
even
PS >.\program.exe 4200000000
odd <---- WRONG!
差不多了!似乎算法在符號性方面有問題,任何超過 2^31 的值似乎都會給出隨機結(jié)果。那么,讓我們來修復(fù)最后一個錯誤:原來 atoi 不能處理無符號性,所以它無法解析大數(shù)字。用 strtoul 代替它就能解決所有問題。
uint32_t number = strtoul(argv[1], , 10);// No problems here
PS >.\program.exe 4200000000
even
PS >.\program.exe 4200000001
odd
順便提一句,這個程序的性能很不錯。對于小數(shù)字,結(jié)果能即時返回,而對于接近 2^32 極限的大數(shù)字,結(jié)果仍能在大約 10 秒內(nèi)返回。考慮到計算機必須從磁盤讀取 40GB 的數(shù)據(jù),將其映射到物理內(nèi)存,然后讓 CPU 在沒有緩存的情況下對其進行處理,老實說這個速度已經(jīng)相當(dāng)令人驚嘆了。作為參考,我電腦的配置為 Core i5 12600K,32GB 內(nèi)存,文件存儲在 M.2 SSD 硬盤上。在計算過程中,我看到 SSD 硬盤的峰值讀取速度約為 800 MB/s。
至此,互聯(lián)網(wǎng)再次被證明是錯誤的:你不僅可以按照 TikTok 帖子的方式編寫一個功能齊全、性能良好的程序,而且還非常有趣。
網(wǎng)友:沒必要,一個簡單的 for 循環(huán)就能解決
不過 Andreas Karlsson 的分享,并沒有得到部分開發(fā)者的認可,甚至認為他有些“嘩眾取寵”:
“在我看來,這幾乎是過度設(shè)計。為什么要費盡心思生成代碼?只需一個簡單的‘for 循環(huán)’就能解決。”
func isOdd(n int) bool {
var odd bool
for i := 0; i < n; i++ {
odd = !odd
}
return odd
}
“真正高質(zhì)量的運行應(yīng)始終使用遞歸。”
func isOdd(n int) bool {
switch {
case n == 0:
return false
case n > 0:
return !isOdd(n-1)
default:
return !isOdd(n+1)
}
}
還有人指出:“我完全聽不懂這個笑話。我們從中學(xué)到了什么?exe 文件不能超過4GB?一個2^32 if 的程序大約是300GB?這看起來并不瘋狂,只是毫無意義。”
對此,有人反駁道:“可笑的是,他真的做到了。幾十年來,人們一直在拿它開玩笑,但這個人是認真的。由于代碼多得太極端,沒有編譯器可以處理,甚至沒有任何已知的匯編程序,因此他必須生成自己的機器碼二進制文件才能運行。結(jié)果最后,他居然還真的成功了。”
參考鏈接:
https://andreasjhkarlsson.github.io/jekyll/update/2023/12/27/4-billion-if-statements.html
https://news.ycombinator.com/item?id=38790597
的一個朋友在最近的一次面試中遇到了一個有趣的 CSS 面試問題。當(dāng)我第一次看到這個問題時,我認為這是一個常見的CSS問題。然而,經(jīng)過仔細研究,我發(fā)現(xiàn)了這個問題的有趣部分。
面試題:請用CSS實現(xiàn)如下效果:
頁面上有一些數(shù)字顯示文章閱讀的數(shù)量。如果數(shù)字小于 100,則數(shù)字的顏色為灰色。如果數(shù)字大于或等于 100,則數(shù)字為棕色,而且這種顏色可以動態(tài)調(diào)整,而不是提前預(yù)設(shè)。
最后面試官要求用純CSS來解決這個問題,你知道如何達到這個效果嗎?
題目分析
這個問題的本質(zhì)是什么?
這個問題的本質(zhì)很簡單,也就是說,這實際上是一個 if-else 問題。
如果我們用偽代碼描述這個問題,它應(yīng)該是這樣的:
let color;
if (reads < 100){
color = 'gray'
} else {
color = 'brown'
}
所以現(xiàn)在問題變成了:我們?nèi)绾卧?CSS 中實現(xiàn)這個 if-else 邏輯?請記住,CSS 中沒有 if-else 關(guān)鍵字之類的東西。
在 CSS 中實現(xiàn) if-else
在 CSS 中實現(xiàn) if-else 的邏輯是本題考查的核心技能。讓我們在下面完成這個邏輯。如果你學(xué)會了這個技巧,你可以用它來實現(xiàn)許多強大的 CSS 效果。
首先,讓我們了解一個叫做clamp的函數(shù)。
clamp() CSS 函數(shù)將一個值限制在上限和下限之間。clamp() 允許在定義的最小值和最大值之間的值范圍內(nèi)選擇中間值。
基本語法格式:
clamp(min, var, max)
我們可以將clamp函數(shù)理解為這樣的偽代碼:
funciton clamp(min, var, max){
if(var <= min){
return min
}
if(var >= max){
return max
}
if(var > min && var < max){
return var
}
}
所以:
clamp(10, 13, 20) → 13
clamp(10, 2, 20) → 10
clamp(10, 30, 20) → 20
用法示例:
font-size 的值不會超過 20px,也不會低于 10px。
這是clamp的基本用法。
如果您對clamp仍有疑問,可以參考 MDN 文檔。
接下來,我們在 CSS 中實現(xiàn)這個功能。
result的值根據(jù) var 的值而變化:
當(dāng) var 的值小于 100 時,結(jié)果的值為 10;
當(dāng) var 的值大于等于 100 時,結(jié)果變?yōu)?20。
如果我們用偽代碼描述這個問題,它應(yīng)該是這樣的:
let result;
if(var < 100){
result = 10
} else {
result = 20
}
這個要求和clamp函數(shù)類似,但又不一樣。clamp可以將 var 的值限制在一個范圍內(nèi),但我們現(xiàn)在希望結(jié)果的值是 10 或 20。
那我們怎么做?
有一個特殊的技巧:我們可以放大 var 的變化,使其值要么達到區(qū)間的上限,要么達到區(qū)間的下限。
于是:
let result = clamp(10, (var-99) * 20, 20)
這會產(chǎn)生一個效果:
用一張圖解釋:
同樣,如果我們希望:
當(dāng) var 的值小于 50 時,result的值為 5。
當(dāng) var 的值大于等于 50 時,result的值為 15。
我們只需要這樣寫:
let result = clamp(5, (var-49) * 15, 15)
你有沒有注意到:這實際上是 if-else 的效果,我們做到了。
在 CSS 中切換顏色
回到最初的面試問題。
為了讓我們后面可以使用 CSS 進行變量計算,我們需要將值放在一個 CSS 變量中,所以 HTML 可以這樣寫:
<num style=<span data-raw-text="" "="" data-textnode-index-1655261252004="96" data-index-1655261252004="1664" class="character" style="margin: 0px; padding: 0px;">"--num:1<span data-raw-text="" "="" data-textnode-index-1655261252004="96" data-index-1655261252004="1672" class="character" style="margin: 0px; padding: 0px;">">1<span>reads</span></num>
<num style=<span data-raw-text="" "="" data-textnode-index-1655261252004="99" data-index-1655261252004="1710" class="character" style="margin: 0px; padding: 0px;">"--num:99<span data-raw-text="" "="" data-textnode-index-1655261252004="99" data-index-1655261252004="1719" class="character" style="margin: 0px; padding: 0px;">">99<span>reads</span></num>
<num style=<span data-raw-text="" "="" data-textnode-index-1655261252004="102" data-index-1655261252004="1758" class="character" style="margin: 0px; padding: 0px;">"--num:102<span data-raw-text="" "="" data-textnode-index-1655261252004="102" data-index-1655261252004="1768" class="character" style="margin: 0px; padding: 0px;">">102<span>reads</span></num>
如果我們不需要考慮 HTML 語義或 SEO 因素,這里的“數(shù)字”和“讀取”都可以由偽元素生成:
<head>
<style>
num::before {
counter-reset: num var(--num);
content: counter(num);
}
num::after {
content: 'reads';
}
</style>
</head>
<body>
<div>
<num style=<span data-raw-text="" "="" data-textnode-index-1655261252004="121" data-index-1655261252004="2033" class="character" style="margin: 0px; padding: 0px;">"--num:1<span data-raw-text="" "="" data-textnode-index-1655261252004="121" data-index-1655261252004="2041" class="character" style="margin: 0px; padding: 0px;">"></num>
<num style=<span data-raw-text="" "="" data-textnode-index-1655261252004="124" data-index-1655261252004="2064" class="character" style="margin: 0px; padding: 0px;">"--num:99<span data-raw-text="" "="" data-textnode-index-1655261252004="124" data-index-1655261252004="2073" class="character" style="margin: 0px; padding: 0px;">"></num>
<num style=<span data-raw-text="" "="" data-textnode-index-1655261252004="127" data-index-1655261252004="2096" class="character" style="margin: 0px; padding: 0px;">"--num:102<span data-raw-text="" "="" data-textnode-index-1655261252004="127" data-index-1655261252004="2106" class="character" style="margin: 0px; padding: 0px;">"></num>
</div>
</body>
如果對 content 和 counter-reset 不熟悉,可以查看 MDN 文檔。
具體的演示效果,可以通過以下地址查看:https://codepen.io/bytefishmedium/pen/VwQrGEb
棕色為#aa540e,用HSL顏色表示為hsl(27, 50%, 36%),如下:
它的飽和度控制顏色的鮮艷程度。飽和度越高,顏色越鮮艷,飽和度越低,顏色越暗。當(dāng)飽和度降低到0時,就變成了完全的灰色,如下:
在灰色和棕色之間切換顏色,即在 hsl(27, 85%, 36%) 和 hsl(27, 85%, 36%) 之間切換。
于是就有如下代碼:
num{
--s: clamp(0%,(var(--num) - 99) * 99%,85%);/* >100 */
color: hsl(27 var(--s) 36%);
}
最終的演示:https://codepen.io/bytefishmedium/pen/WNMXabm
總結(jié)
我們通過clamp函數(shù)在CSS中實現(xiàn)if-else效果,最后讓顏色根據(jù)變量的值進行切換。
其實原面試題還有另外一部分,簡單來說就是:讓顏色在多個值之間切換。僅使用 if-else 不足以滿足此要求,有興趣的話,可以留言交流學(xué)習(xí)。
-
近在群里,有個小伙伴問了這么一道很有趣的問題:
為了簡化實際效果,我們看這么一張示意效果圖:
可以看到,當(dāng)容器高度沒有超過某一個值時,沒有箭頭圖標。反之,箭頭圖標出現(xiàn)。
這個效果在很多場景都會出現(xiàn),可以算是一個高頻場景,那么在今天,我們能否不使用 JavaScript,僅僅憑借 CSS 實現(xiàn)類似于這樣的功能呢?
答案當(dāng)然是可以,XBoxYan 大佬在 CSS 實現(xiàn)超過固定高度后出現(xiàn)展開折疊按鈕 介紹了一種非常巧妙的借助浮動的解法,十分有意思,感興趣的同學(xué)可以先行一步了解。
當(dāng)然,浮動 float 在現(xiàn)如今的 CSS 世界,運用的已經(jīng)非常少了。那么除了浮動,還有沒有其它有意思的解法?本文我們將一起來探究探究。
第一種方法,非常簡單,但是對兼容性有所要求。那就是使用容器查詢 -- @container 語法。
容器查詢在 新時代布局新特性 -- 容器查詢 也詳細介紹過。
簡單而言,容器查詢它給予了 CSS,在不改變?yōu)g覽器視口寬度的前提下,只是根據(jù)容器的寬度或者高度變化,對布局做調(diào)整的能力。
基于這個場景,我們假設(shè)我們有如下的 HTML/CSS 結(jié)構(gòu):
<div class="g-container">
<div class="g-content">
Lorem ipsum dolor s...
</div>
</div>
.g-container {
position: relative;
width: 300px;
height: 300px;
resize: vertical;
overflow: hidden;
.g-content {
height: 100%;
}
.g-content::before {
content: "↑";
position: absolute;
bottom: 0px;
left: 50%;
transform: translate(-50%, 0);
}
}
它是這么一個樣式效果:
其中,我們給元素 .g-content 添加了 resize: vertical,讓它變成了一個可以在豎直方向上通過拖動改變高度的容器,以模擬容器在不同內(nèi)容的場景下,高度不一致的問題:
我們通過元素的偽元素實現(xiàn)了箭頭 ICON,并且它是一直顯示在容器內(nèi)的。
下面,我們通過簡單的幾句容器查詢代碼,就可以實現(xiàn)讓箭頭 ICON,只在容器高度超過特定高度的時候才出現(xiàn):
.g-container {
container-type: size;
container-name: container;
}
@container container (height <= 260px) {
.g-content::before {
opacity: 0;
}
}
簡單解釋一下:
這樣,我們就非常簡單的實現(xiàn)了容器在不同高度下,ICON 元素的顯示隱藏切換:
完整的代碼,你可以戳這里:CodePen Demo -- flexible content
當(dāng)然,這個方案的唯一缺點在于,截止至今天(2023-11-11),兼容性不是那么好:
那,有沒有兼容性更好的方案?當(dāng)然,來我們一起來看看 clamp + calc 的方案。
上面效果的核心在于:
那么想想看,如果拿容器的高度減去一個固定的高度值,會發(fā)生什么?假設(shè)一下,ICON 元素的 CSS 代碼如下:
.g-content::before {
content: "↑";
position: absolute;
left: 50%;
transform: translate(-50%, 0);
bottom: calc(100% - 200px);
}
仔細觀察 bottom: calc(100% - 200px),在元素的 bottom 屬性中,100% 表示的是容器當(dāng)前的高度,因此 calc(100% - 200px) 的含義就代表,容器當(dāng)前高度減去一個固定高度 200px。因此:
我們看看這種情況下,整個 ICON 的表現(xiàn)是如何的:
可以看到,當(dāng)容器高度大于 200px 的時候,箭頭 ICON 確實出現(xiàn)了,但是,它無法一直定位在整個容器的最下方。
有什么辦法讓它在出現(xiàn)后,一直定位在容器的最下方嗎?
別忘了,CSS 中,還有幾個非常有意思的數(shù)學(xué)函數(shù):min()、max()、clamp(),它們可以有效限定動態(tài)值在某個范圍之內(nèi)!
不太了解的,可以看看這篇 現(xiàn)代 CSS 解決方案:CSS 數(shù)學(xué)函數(shù)
利用 clamp(),我們可以限定計算值的最大最小范圍,在這個場景下,我們可以限制 bottom 的最大值為 10px:
.g-content::before {
// ...
bottom: clamp(-99999px, calc(100% - 200px), 10px);
}
上面的代碼 clamp(-99999px, calc(100% - 200px), 10px),核心在于,如果 calc(100% - 200px) 的計算值大于 10px,它只會取值為 10px,利用這個技巧,我們可以在容器高度超長時,把箭頭 ICON 牢牢釘在容器的下方,無論容器的高度是多少:
到此,結(jié)束了嗎?顯然沒有。
雖然上面的代碼,解決當(dāng) calc(100% - 200px) 的計算值大于 10px 的場景,但是沒有解決,當(dāng) calc(100% - 200px) 的計算值處于 -10px ~ 10px 這個范圍內(nèi)的問題。
我們可以清楚的看到,當(dāng)我們往下拖動容器變高的時候,箭頭元素是逐漸慢慢向上出現(xiàn),而不是突然在某一個高度下,直接出現(xiàn),所以在實際使用中,會出現(xiàn)這種 ICON 只出現(xiàn)了一半的尷尬場景:
但是,莫慌!這個問題也好解決,我們只需要給 calc(100% - 200px) 的計算值,乘上一個超級大的倍數(shù)即可。原因在于:
看看代碼,此時,整個 bottom 的取值就改造成了:
.g-content::before {
// ...
bottom: clamp(-9999px, calc(calc(100% - 200px) * 100000), 10px);
}
通過,將 calc(100% - 200px) 的值,乘上一個超大的倍數(shù) 100000,無論是正值還是負值,我們把計算值放大了 100000 倍。這樣,整個效果就達成了我們想要的效果:
仔細看上圖,ICON 元素從漸現(xiàn),變成了瞬間出現(xiàn)!與上面的 @container 效果幾乎一致,最終達成了我們想要的效果。
其核心就在于 clamp(-9999px, calc(calc(100% - 200px) * 100000), 10px),一定需要好好理解這一段代碼背后的邏輯。
基于此,我們就巧妙的利用 clamp() + calc() 方法,近似的實現(xiàn)了類似于 if/else 的邏輯,實在是妙不可言!
CodePen Demo -- flexible content
原文鏈接:https://www.cnblogs.com/coco1s/p/17831064.html
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。