看到這個標題,相信大多數人的第一反應是:真的有人用 40 億條 if 語句,只為判斷一個數字是奇數還是偶數?的確有,這個開發者名為 Andreas Karlsson,他還把整個過程都整理成文了。
或許由于這“40 億條 if 語句”聽起來實在震撼,Andreas Karlsson 分享的這篇文章在 Hacker News 上很快引起了極大的關注和討論,而他在文中也直白表示:其實這個想法,最初源于一個充滿惡評的短視頻。
以下為譯文:
大于 11 的數字,沒有輸出結果
我最近在火車上刷手機時,偶然發現了上面這個截圖:“寫了一個程序,來判斷一個數字是偶數還是奇數。”點開評論區,果然是一連串的惡意評論,多數都在嘲笑這位新手程序員的稚嫩和無知,竟企圖以這種方式解決計算機科學中的經典問題“取模運算”。
可看過截圖中的代碼和網友評論后,我莫名生出了一些不同的想法:現在,AI 正在分分鐘取代程序員、搶走他們的飯碗,并徹底改變了我們對代碼的思考方式,或許我們應該更加開放地接受這個行業新生代的思想?
其實仔細想來,上述代碼是時間和空間的一種完美權衡:你在付出自己時間的同時,也換來了計算機的內存和時間——這難道不是一個神奇的算法嗎?
于是,我開始探索這種只使用比較來判斷一個數字是奇數還是偶數的想法,看看它在實際情況中的效果到底如何。由于我是一位高性能代碼的忠實擁護者,因此我決定用 C 語言來實現這個想法。
然后,我就開始編碼了:
/* 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 禁用優化,確保煩人的編譯器不會干擾我們的算法。編譯完成后,我們就可以對程序進行快速測試,看看結果如何:
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
結果顯示:0、4 是偶數,3、7 是奇數。這么看來,程序似乎運行得挺好,但在進一步測試后,我發現了一些問題:
PS > .\program.exe 50
PS > .\program.exe 11
PS > .\program.exe 99
大于 11 的數字沒有輸出,看來這個程序只對 11 以下的數字有效!回到原始代碼中,可以發現問題出在最后一個 if 語句之后:我們需要更多的 if 語句!
向 32 位(32-bit)數擴展
這件事進行到這里,就需要我在時間和內存之間做出權衡了。考慮到我的壽命有限,我決定用另一種編程語言對 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("}")
好了!現在我們可以生成一個程序,解決所有 8 位(8-bit)整數的奇偶問題!
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
看看,這個效果簡直完美!現在,讓我們把它放大到 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
太棒了,我們的算法似乎能夠處理大量數據!可執行文件大約只有 2 MB,但這與我擁有高達 31.8 GB 內存的強大游戲設備相比,簡直不值一提。
但眾所周知,32 位(32-bit)才是計算機領域的終極目標,也是我們解決所有實際工程和科學問題所需的最終位寬。畢竟,在 IPv4 因所謂的 "地址耗盡 "而被認為過時 60 年后,它如今仍然很強大。所以,讓我們來看看最終的規模:32 位的數字是 16 位的 65536 倍,這會有什么問題嗎?
print(" uint32_t number = atoi(argv[1]); // No problems here")
…
for i in range(2**32):
于是,我讓強大的 Python 開始它的工作。48 小時后,我喝了一杯咖啡,然后回來檢查程序,就得到了一個美麗的 c 文件,大小接近 330 GB!我幾乎可以肯定,這是歷史上最大的 c 文件之一。當我輸入下一條命令時,我的手指都在顫抖,我猜 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 可移植可執行文件格式(.exe)的限制時,我發現它無法處理超過 4GB 的文件!由于需要將 40 多億次比較語句編碼到可執行文件中,這對于實現我們的算法是一個主要障礙。即使每次比較時使用的字節數少于一個,對我來說工作量也太大了。
不過,糟糕的編譯器和文件格式不應該阻止我們實現夢想。畢竟,編譯器所做的只是將一些花哨的機器代碼寫入文件,而文件格式只是一些結構,告訴操作系統如何將二進制代碼放入內存——其實,我們自己就能做到。
解決最后一個問題,程序性能很不錯
讓我們先用 x86-64 匯編語言編寫一個 IsEven 函數,因為這是我 Intel 處理器驅動的本地語言,它看起來是這樣的:
; 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
這并不是真正正確的匯編代碼,但這不重要,因為我們要手動將其編譯成機器代碼。
你問我是怎么做到的?我上網查閱了x86(-64) 體系結構手冊,還利用我早年編寫仿真器和黑客經驗,找出了每條指令的正確操作碼和格式……開個玩笑,這是不可能的。實際上,我是直接問 ChatGPT 每條指令的正確操作碼是什么,幸運的是,它也沒有產生 x86-64 的任何新擴展。
所以現在我們只需編寫一個“編譯器”來輸出這段代碼。請注意,我們將直接使用從 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 帖子的最初構想,但本質并沒有改變:我們創建了一個非常長的 if 語句列表,用于確定某個數字是奇數還是偶數,并忽略了任何有助于簡化問題的算術運算。
運行這個程序后,我們就得到了一個 40GB 的文件,其中包含了確定 32 位數字是偶數還是奇數所需的全部 42 億次比較!現在,我們只需編寫能夠加載和使用這些指令的主程序。為了提高性能(這一點非常重要),我決定將文件映射到地址空間,而非一次性讀取全部文件。這樣,我們就可以假裝整個文件已經在內存中,讓可憐的操作系統來處理將一個 40GB 的 Blob 裝入虛擬內存的問題。用 READ 和 EXECUTE 權限映射文件后,我們就可以使用函數指針調用代碼了,代碼如下:
#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);
}
就是這樣!現在我們已經具備了判斷任何 32 位(32-bit)數字是奇是偶的所有功能,讓我們試一試:
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 的值似乎都會給出隨機結果。那么,讓我們來修復最后一個錯誤:原來 atoi 不能處理無符號性,所以它無法解析大數字。用 strtoul 代替它就能解決所有問題。
uint32_t number = strtoul(argv[1], , 10);// No problems here
PS >.\program.exe 4200000000
even
PS >.\program.exe 4200000001
odd
順便提一句,這個程序的性能很不錯。對于小數字,結果能即時返回,而對于接近 2^32 極限的大數字,結果仍能在大約 10 秒內返回。考慮到計算機必須從磁盤讀取 40GB 的數據,將其映射到物理內存,然后讓 CPU 在沒有緩存的情況下對其進行處理,老實說這個速度已經相當令人驚嘆了。作為參考,我電腦的配置為 Core i5 12600K,32GB 內存,文件存儲在 M.2 SSD 硬盤上。在計算過程中,我看到 SSD 硬盤的峰值讀取速度約為 800 MB/s。
至此,互聯網再次被證明是錯誤的:你不僅可以按照 TikTok 帖子的方式編寫一個功能齊全、性能良好的程序,而且還非常有趣。
網友:沒必要,一個簡單的 for 循環就能解決
不過 Andreas Karlsson 的分享,并沒有得到部分開發者的認可,甚至認為他有些“嘩眾取寵”:
“在我看來,這幾乎是過度設計。為什么要費盡心思生成代碼?只需一個簡單的‘for 循環’就能解決。”
func isOdd(n int) bool {
var odd bool
for i := 0; i < n; i++ {
odd = !odd
}
return odd
}
“真正高質量的運行應始終使用遞歸。”
func isOdd(n int) bool {
switch {
case n == 0:
return false
case n > 0:
return !isOdd(n-1)
default:
return !isOdd(n+1)
}
}
還有人指出:“我完全聽不懂這個笑話。我們從中學到了什么?exe 文件不能超過4GB?一個2^32 if 的程序大約是300GB?這看起來并不瘋狂,只是毫無意義。”
對此,有人反駁道:“可笑的是,他真的做到了。幾十年來,人們一直在拿它開玩笑,但這個人是認真的。由于代碼多得太極端,沒有編譯器可以處理,甚至沒有任何已知的匯編程序,因此他必須生成自己的機器碼二進制文件才能運行。結果最后,他居然還真的成功了。”
參考鏈接:
https://andreasjhkarlsson.github.io/jekyll/update/2023/12/27/4-billion-if-statements.html
https://news.ycombinator.com/item?id=38790597
的一個朋友在最近的一次面試中遇到了一個有趣的 CSS 面試問題。當我第一次看到這個問題時,我認為這是一個常見的CSS問題。然而,經過仔細研究,我發現了這個問題的有趣部分。
面試題:請用CSS實現如下效果:
頁面上有一些數字顯示文章閱讀的數量。如果數字小于 100,則數字的顏色為灰色。如果數字大于或等于 100,則數字為棕色,而且這種顏色可以動態調整,而不是提前預設。
最后面試官要求用純CSS來解決這個問題,你知道如何達到這個效果嗎?
題目分析
這個問題的本質是什么?
這個問題的本質很簡單,也就是說,這實際上是一個 if-else 問題。
如果我們用偽代碼描述這個問題,它應該是這樣的:
let color;
if (reads < 100){
color = 'gray'
} else {
color = 'brown'
}
所以現在問題變成了:我們如何在 CSS 中實現這個 if-else 邏輯?請記住,CSS 中沒有 if-else 關鍵字之類的東西。
在 CSS 中實現 if-else
在 CSS 中實現 if-else 的邏輯是本題考查的核心技能。讓我們在下面完成這個邏輯。如果你學會了這個技巧,你可以用它來實現許多強大的 CSS 效果。
首先,讓我們了解一個叫做clamp的函數。
clamp() CSS 函數將一個值限制在上限和下限之間。clamp() 允許在定義的最小值和最大值之間的值范圍內選擇中間值。
基本語法格式:
clamp(min, var, max)
我們可以將clamp函數理解為這樣的偽代碼:
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 中實現這個功能。
result的值根據 var 的值而變化:
當 var 的值小于 100 時,結果的值為 10;
當 var 的值大于等于 100 時,結果變為 20。
如果我們用偽代碼描述這個問題,它應該是這樣的:
let result;
if(var < 100){
result = 10
} else {
result = 20
}
這個要求和clamp函數類似,但又不一樣。clamp可以將 var 的值限制在一個范圍內,但我們現在希望結果的值是 10 或 20。
那我們怎么做?
有一個特殊的技巧:我們可以放大 var 的變化,使其值要么達到區間的上限,要么達到區間的下限。
于是:
let result = clamp(10, (var-99) * 20, 20)
這會產生一個效果:
用一張圖解釋:
同樣,如果我們希望:
當 var 的值小于 50 時,result的值為 5。
當 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 因素,這里的“數字”和“讀取”都可以由偽元素生成:
<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%),如下:
它的飽和度控制顏色的鮮艷程度。飽和度越高,顏色越鮮艷,飽和度越低,顏色越暗。當飽和度降低到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
總結
我們通過clamp函數在CSS中實現if-else效果,最后讓顏色根據變量的值進行切換。
其實原面試題還有另外一部分,簡單來說就是:讓顏色在多個值之間切換。僅使用 if-else 不足以滿足此要求,有興趣的話,可以留言交流學習。
-
近在群里,有個小伙伴問了這么一道很有趣的問題:
為了簡化實際效果,我們看這么一張示意效果圖:
可以看到,當容器高度沒有超過某一個值時,沒有箭頭圖標。反之,箭頭圖標出現。
這個效果在很多場景都會出現,可以算是一個高頻場景,那么在今天,我們能否不使用 JavaScript,僅僅憑借 CSS 實現類似于這樣的功能呢?
答案當然是可以,XBoxYan 大佬在 CSS 實現超過固定高度后出現展開折疊按鈕 介紹了一種非常巧妙的借助浮動的解法,十分有意思,感興趣的同學可以先行一步了解。
當然,浮動 float 在現如今的 CSS 世界,運用的已經非常少了。那么除了浮動,還有沒有其它有意思的解法?本文我們將一起來探究探究。
第一種方法,非常簡單,但是對兼容性有所要求。那就是使用容器查詢 -- @container 語法。
容器查詢在 新時代布局新特性 -- 容器查詢 也詳細介紹過。
簡單而言,容器查詢它給予了 CSS,在不改變瀏覽器視口寬度的前提下,只是根據容器的寬度或者高度變化,對布局做調整的能力。
基于這個場景,我們假設我們有如下的 HTML/CSS 結構:
<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,讓它變成了一個可以在豎直方向上通過拖動改變高度的容器,以模擬容器在不同內容的場景下,高度不一致的問題:
我們通過元素的偽元素實現了箭頭 ICON,并且它是一直顯示在容器內的。
下面,我們通過簡單的幾句容器查詢代碼,就可以實現讓箭頭 ICON,只在容器高度超過特定高度的時候才出現:
.g-container {
container-type: size;
container-name: container;
}
@container container (height <= 260px) {
.g-content::before {
opacity: 0;
}
}
簡單解釋一下:
這樣,我們就非常簡單的實現了容器在不同高度下,ICON 元素的顯示隱藏切換:
完整的代碼,你可以戳這里:CodePen Demo -- flexible content
當然,這個方案的唯一缺點在于,截止至今天(2023-11-11),兼容性不是那么好:
那,有沒有兼容性更好的方案?當然,來我們一起來看看 clamp + calc 的方案。
上面效果的核心在于:
那么想想看,如果拿容器的高度減去一個固定的高度值,會發生什么?假設一下,ICON 元素的 CSS 代碼如下:
.g-content::before {
content: "↑";
position: absolute;
left: 50%;
transform: translate(-50%, 0);
bottom: calc(100% - 200px);
}
仔細觀察 bottom: calc(100% - 200px),在元素的 bottom 屬性中,100% 表示的是容器當前的高度,因此 calc(100% - 200px) 的含義就代表,容器當前高度減去一個固定高度 200px。因此:
我們看看這種情況下,整個 ICON 的表現是如何的:
可以看到,當容器高度大于 200px 的時候,箭頭 ICON 確實出現了,但是,它無法一直定位在整個容器的最下方。
有什么辦法讓它在出現后,一直定位在容器的最下方嗎?
別忘了,CSS 中,還有幾個非常有意思的數學函數:min()、max()、clamp(),它們可以有效限定動態值在某個范圍之內!
不太了解的,可以看看這篇 現代 CSS 解決方案:CSS 數學函數
利用 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 牢牢釘在容器的下方,無論容器的高度是多少:
到此,結束了嗎?顯然沒有。
雖然上面的代碼,解決當 calc(100% - 200px) 的計算值大于 10px 的場景,但是沒有解決,當 calc(100% - 200px) 的計算值處于 -10px ~ 10px 這個范圍內的問題。
我們可以清楚的看到,當我們往下拖動容器變高的時候,箭頭元素是逐漸慢慢向上出現,而不是突然在某一個高度下,直接出現,所以在實際使用中,會出現這種 ICON 只出現了一半的尷尬場景:
但是,莫慌!這個問題也好解決,我們只需要給 calc(100% - 200px) 的計算值,乘上一個超級大的倍數即可。原因在于:
看看代碼,此時,整個 bottom 的取值就改造成了:
.g-content::before {
// ...
bottom: clamp(-9999px, calc(calc(100% - 200px) * 100000), 10px);
}
通過,將 calc(100% - 200px) 的值,乘上一個超大的倍數 100000,無論是正值還是負值,我們把計算值放大了 100000 倍。這樣,整個效果就達成了我們想要的效果:
仔細看上圖,ICON 元素從漸現,變成了瞬間出現!與上面的 @container 效果幾乎一致,最終達成了我們想要的效果。
其核心就在于 clamp(-9999px, calc(calc(100% - 200px) * 100000), 10px),一定需要好好理解這一段代碼背后的邏輯。
基于此,我們就巧妙的利用 clamp() + calc() 方法,近似的實現了類似于 if/else 的邏輯,實在是妙不可言!
CodePen Demo -- flexible content
原文鏈接:https://www.cnblogs.com/coco1s/p/17831064.html
*請認真填寫需求信息,我們會在24小時內與您取得聯系。