Tạo Đồng hồ với Hàm lượng giác sin() và cos() CSS mới

Tạo Đồng hồ với Hàm lượng giác sin() và cos() CSS mới

Nút nguồn: 1999799

Các hàm lượng giác CSS có ở đây! Chà, chúng là nếu bạn đang sử dụng các phiên bản Firefox và Safari mới nhất. Có loại sức mạnh toán học này trong CSS sẽ mở ra rất nhiều khả năng. Trong hướng dẫn này, tôi nghĩ chúng ta sẽ nhúng ngón chân vào nước để cảm nhận một số chức năng mới hơn: sin()cos().

Có các chức năng lượng giác khác trong đường ống - bao gồm tan() - vậy tại sao chỉ tập trung vào sin()cos()? Chúng tình cờ trở nên hoàn hảo cho ý tưởng mà tôi có trong đầu, đó là đặt văn bản dọc theo cạnh của một vòng tròn. Điều đó đã được đề cập ở đây trên Thủ thuật CSS khi Chris đã chia sẻ một cách tiếp cận sử dụng Sass mixin. Đó là sáu năm trước, vì vậy chúng ta hãy điều trị nó bằng cách chữa trị.

Đây là những gì tôi có trong tâm trí. Một lần nữa, nó chỉ được hỗ trợ trong Firefox và Safari vào lúc này:

Vì vậy, nó không hoàn toàn giống như các từ tạo thành một hình tròn, mà chúng ta đang đặt các ký tự văn bản dọc theo hình tròn để tạo thành một mặt đồng hồ. Đây là một số đánh dấu chúng ta có thể sử dụng để khởi động mọi thứ:

<div class="clock"> <div class="clock-face"> <time datetime="12:00">12</time> <time datetime="1:00">1</time> <time datetime="2:00">2</time> <time datetime="3:00">3</time> <time datetime="4:00">4</time> <time datetime="5:00">5</time> <time datetime="6:00">6</time> <time datetime="7:00">7</time> <time datetime="8:00">8</time> <time datetime="9:00">9</time> <time datetime="10:00">10</time> <time datetime="11:00">11</time> </div>
</div>

Tiếp theo, đây là một số phong cách siêu cơ bản cho .clock-face thùng đựng hàng. tôi quyết định sử dụng <time> gắn thẻ với một datetime thuộc tính. 

.clock { --_ow: clamp(5rem, 60vw, 40rem); --_w: 88cqi; aspect-ratio: 1; background-color: tomato; border-radius: 50%; container-type: inline; display: grid; height: var(--_ow); place-content: center; position: relative; width var(--_ow);
}

Tôi đã trang trí mọi thứ một chút trong đó, nhưng chỉ để có được hình dạng cơ bản và màu nền để giúp chúng tôi thấy những gì chúng tôi đang làm. Lưu ý cách chúng tôi lưu width giá trị trong một Biến CSS. Chúng ta sẽ sử dụng nó sau. Không có nhiều để xem xét cho đến nay:

Vòng tròn lớn màu cà chua với danh sách dọc các số 1-12 ở bên trái.

Nó trông giống như một số loại thử nghiệm nghệ thuật hiện đại, phải không? Hãy giới thiệu một biến mới, --_r, để lưu trữ vòng tròn bán kính, bằng một nửa chiều rộng của hình tròn. Bằng cách này, nếu chiều rộng (--_w) thay đổi, giá trị bán kính (--_r) cũng sẽ cập nhật — nhờ một hàm toán học CSS khác, calc():

.clock { --_w: 300px; --_r: calc(var(--_w) / 2); /* rest of styles */
}

Bây giờ, một chút toán học. Một vòng tròn là 360 độ. Chúng tôi có 12 nhãn trên đồng hồ của mình, vì vậy muốn đặt các số cứ sau 30 độ (360 / 12). Ở vùng đất toán học, một vòng tròn bắt đầu lúc 3 giờ, vì vậy buổi trưa thực sự là âm 90 độ từ đó, là 270 độ (360 - 90).

Hãy thêm một biến khác, --_d, mà chúng ta có thể sử dụng để thiết lập một trình độ giá trị từng con số trên mặt đồng hồ. Chúng tôi sẽ tăng các giá trị thêm 30 độ để hoàn thành vòng kết nối của chúng tôi:

.clock time:nth-child(1) { --_d: 270deg; }
.clock time:nth-child(2) { --_d: 300deg; }
.clock time:nth-child(3) { --_d: 330deg; }
.clock time:nth-child(4) { --_d: 0deg; }
.clock time:nth-child(5) { --_d: 30deg; }
.clock time:nth-child(6) { --_d: 60deg; }
.clock time:nth-child(7) { --_d: 90deg; }
.clock time:nth-child(8) { --_d: 120deg; }
.clock time:nth-child(9) { --_d: 150deg; }
.clock time:nth-child(10) { --_d: 180deg; }
.clock time:nth-child(11) { --_d: 210deg; }
.clock time:nth-child(12) { --_d: 240deg; }

OK, bây giờ là lúc để làm bẩn tay chúng ta với sin()cos() chức năng! Những gì chúng tôi muốn làm là sử dụng chúng để lấy tọa độ X và Y cho mỗi số để chúng tôi có thể đặt chúng đúng cách xung quanh mặt đồng hồ.

Công thức tọa độ X là radius + (radius * cos(degree)). Hãy cắm nó vào cái mới của chúng ta --_x biến:

--_x: calc(var(--_r) + (var(--_r) * cos(var(--_d))));

Công thức cho tọa độ Y là radius + (radius * sin(degree)). Chúng tôi có những gì chúng tôi cần để tính toán rằng:

--_y: calc(var(--_r) + (var(--_r) * sin(var(--_d))));

Có một vài điều chúng ta cần làm để thiết lập các số, vì vậy, hãy đặt một số kiểu dáng cơ bản cho chúng để đảm bảo chúng được định vị và đặt hoàn toàn với tọa độ của chúng ta:

.clock-face time { --_x: calc(var(--_r) + (var(--_r) * cos(var(--_d)))); --_y: calc(var(--_r) + (var(--_r) * sin(var(--_d)))); --_sz: 12cqi; display: grid; height: var(--_sz); left: var(--_x); place-content: center; position: absolute; top: var(--_y); width: var(--_sz);
}

Chú ý --_sz, mà chúng tôi sẽ sử dụng cho widthheight của các con số trong một thời điểm. Hãy xem những gì chúng ta có cho đến nay.

Vòng tròn lớn màu cà chua với các nhãn số giờ lệch tâm dọc theo cạnh của nó.

Điều này chắc chắn trông giống như một chiếc đồng hồ! Xem cách góc trên cùng bên trái của mỗi số được đặt đúng vị trí xung quanh vòng tròn? Chúng ta cần “thu nhỏ” bán kính khi tính toán vị trí cho mỗi số. Chúng ta có thể khấu trừ kích thước của một số (--_sz) từ kích thước của vòng tròn (--_w), trước khi tính bán kính:

--_r: calc((var(--_w) - var(--_sz)) / 2);
Vòng tròn lớn màu cà chua với nhãn số giờ dọc theo cạnh tròn của nó.

Tốt hơn nhiều! Hãy thay đổi màu sắc, để nó trông thanh lịch hơn:

Mặt đồng hồ màu trắng với các số trên nền màu xám đậm. Đồng hồ không có kim.

Chúng ta có thể dừng ngay tại đây! Chúng tôi đã hoàn thành mục tiêu đặt văn bản xung quanh một vòng tròn, phải không? Nhưng đồng hồ không có kim để hiển thị giờ, phút và giây là gì?

Hãy sử dụng một hình ảnh động CSS duy nhất cho điều đó. Trước tiên, hãy thêm ba phần tử nữa vào phần đánh dấu của chúng ta,

<div class="clock"> <!-- after <time>-tags --> <span class="arm seconds"></span> <span class="arm minutes"></span> <span class="arm hours"></span> <span class="arm center"></span>
</div>

Sau đó, một số đánh dấu chung cho cả ba nhánh. Một lần nữa, hầu hết điều này chỉ đảm bảo rằng các cánh tay được định vị và đặt hoàn toàn phù hợp:

.arm { background-color: var(--_abg); border-radius: calc(var(--_aw) * 2); display: block; height: var(--_ah); left: calc((var(--_w) - var(--_aw)) / 2); position: absolute; top: calc((var(--_w) / 2) - var(--_ah)); transform: rotate(0deg); transform-origin: bottom; width: var(--_aw);
}

Chúng tôi sẽ sử dụng hoạt hình tương tự cho cả ba cánh tay:

@keyframes turn { to { transform: rotate(1turn); }
}

Sự khác biệt duy nhất là thời gian mà các cánh tay riêng lẻ cần để thực hiện một lượt hoàn chỉnh. Cho cánh tay giờ, nó mất 12 giờ để thực hiện một lượt đầy đủ. Các animation-duration thuộc tính chỉ chấp nhận các giá trị tính bằng mili giây và giây. Hãy giữ nguyên số giây, tức là 43,200 giây (60 seconds * 60 minutes * 12 hours).

animation: turn 43200s infinite;

Phải mất 1 giờ cho cánh tay phút để thực hiện một lượt đầy đủ. Nhưng chúng tôi muốn đây là một hoạt hình nhiều bước vì vậy chuyển động giữa các cánh tay là so le chứ không phải tuyến tính. Chúng ta sẽ cần 60 bước, mỗi bước một phút:

animation: turn 3600s steps(60, end) infinite;

Sản phẩm cánh tay giây is hầu như giống nhau dưới dạng nhánh phút, nhưng thời lượng là 60 giây thay vì 60 phút:

animation: turn 60s steps(60, end) infinite;

Hãy cập nhật các thuộc tính mà chúng ta đã tạo theo các kiểu phổ biến:

.seconds { --_abg: hsl(0, 5%, 40%); --_ah: 145px; --_aw: 2px; animation: turn 60s steps(60, end) infinite;
}
.minutes { --_abg: #333; --_ah: 145px; --_aw: 6px; animation: turn 3600s steps(60, end) infinite;
}
.hours { --_abg: #333; --_ah: 110px; --_aw: 6px; animation: turn 43200s linear infinite;
}

Nếu chúng ta muốn bắt đầu tại thời điểm hiện tại thì sao? Chúng tôi cần một chút JavaScript:

const time = new Date();
const hour = -3600 * (time.getHours() % 12);
const mins = -60 * time.getMinutes();
app.style.setProperty('--_dm', `${mins}s`);
app.style.setProperty('--_dh', `${(hour+mins)}s`);

tôi đã thêm id="app" vào mặt đồng hồ và đặt hai thuộc tính tùy chỉnh mới trên đó để đặt giá trị âm animation-delay, như Mate Marschalko đã làm khi anh ấy chia sẻ đồng hồ chỉ dành cho CSS. Các getHours() phương pháp của JavaScipt Date đối tượng đang sử dụng định dạng 24 giờ, vì vậy chúng tôi sử dụng remainder nhà điều hành để chuyển đổi nó thành định dạng 12 giờ.

Trong CSS, chúng ta cần thêm animation-delay cũng:

.minutes { animation-delay: var(--_dm, 0s); /* other styles */
} .hours { animation-delay: var(--_dh, 0s); /* other styles */
}

Chỉ một điều nữa. Sử dụng CSS @supports và các thuộc tính chúng tôi đã tạo, chúng tôi có thể cung cấp dự phòng cho các trình duyệt không hỗ trợ sin() và cos(). (Cảm ơn Temani Afif!):

@supports not (left: calc(1px * cos(45deg))) {
  time {
    left: 50% !important;
    top: 50% !important;
    transform: translate(-50%,-50%) rotate(var(--_d)) translate(var(--_r)) rotate(calc(-1*var(--_d)))
  }
}

Và Voila! Đồng hồ của chúng ta đã xong! Đây là bản demo cuối cùng một lần nữa. Một lần nữa, nó chỉ được hỗ trợ trong Firefox và Safari vào lúc này.

Ta còn làm gì khác được nữa?

Chỉ loay hoay ở đây thôi, nhưng chúng ta có thể nhanh chóng biến chiếc đồng hồ của mình thành một thư viện ảnh hình tròn bằng cách thay thế <time> thẻ với <img> sau đó cập nhật chiều rộng (--_w) và bán kính (--_r) giá trị:

Hãy thử một lần nữa. Tôi đã đề cập trước đó về việc đồng hồ trông giống như một thử nghiệm nghệ thuật hiện đại. Chúng ta có thể dựa vào đó và tạo lại một mẫu mà tôi đã thấy trên áp phích (mà rất tiếc là tôi đã không mua) trong một phòng trưng bày nghệ thuật vào ngày hôm trước. Như tôi nhớ, nó được gọi là “Mặt trăng” và bao gồm một loạt các chấm tạo thành một vòng tròn.

Một vòng tròn lớn được hình thành từ một loạt các vòng tròn nhỏ hơn có nhiều màu đất khác nhau.

Lần này chúng ta sẽ sử dụng danh sách không có thứ tự vì các vòng kết nối không tuân theo một thứ tự cụ thể nào. Chúng tôi thậm chí sẽ không đưa tất cả các mục danh sách vào đánh dấu. Thay vào đó, hãy thêm JavaScript vào chúng và thêm một vài điều khiển mà chúng ta có thể sử dụng để thao tác với kết quả cuối cùng.

Các điều khiển là đầu vào phạm vi (<input type="range">) mà chúng ta sẽ bọc trong một <form> và lắng nghe input biến cố.

<form id="controls"> <fieldset> <label>Number of rings <input type="range" min="2" max="12" value="10" id="rings" /> </label> <label>Dots per ring <input type="range" min="5" max="12" value="7" id="dots" /> </label> <label>Spread <input type="range" min="10" max="40" value="40" id="spread" /> </label> </fieldset>
</form>

Chúng ta sẽ chạy phương thức này trên “đầu vào”, điều này sẽ tạo ra một loạt các <li> các phần tử có hoành độ (--_d) biến chúng tôi đã sử dụng trước đó được áp dụng cho từng biến. Chúng tôi cũng có thể sử dụng lại biến bán kính của mình (--_r).

Tôi cũng muốn các dấu chấm có màu khác nhau. Vì vậy, hãy chọn ngẫu nhiên (tốt, không hoàn toàn ngẫu nhiên) giá trị màu HSL cho từng mục danh sách và lưu trữ dưới dạng một biến CSS mới, --_bgc:

const update = () => { let s = ""; for (let i = 1; i <= rings.valueAsNumber; i++) { const r = spread.valueAsNumber * i; const theta = coords(dots.valueAsNumber * i); for (let j = 0; j < theta.length; j++) { s += `<li style="--_d:${theta[j]};--_r:${r}px;--_bgc:hsl(${random( 50, 25 )},${random(90, 50)}%,${random(90, 60)}%)"></li>`; } } app.innerHTML = s;
}

Sản phẩm random() phương thức chọn một giá trị trong một phạm vi số xác định:

const random = (max, min = 0, f = true) => f ? Math.floor(Math.random() * (max - min) + min) : Math.random() * max;

Và thế là xong. Chúng tôi sử dụng JavaScript để hiển thị đánh dấu, nhưng ngay sau khi nó được hiển thị, chúng tôi không thực sự cần nó. Các sin()cos() các chức năng giúp chúng tôi định vị tất cả các dấu chấm ở đúng vị trí.

Lời cuối

Đặt mọi thứ xung quanh một vòng tròn là một ví dụ khá cơ bản để chứng minh sức mạnh của các hàm lượng giác như sin()cos(). Nhưng no la có thật không thật thú vị khi chúng tôi đang nhận được các tính năng CSS hiện đại cung cấp các giải pháp mới cho các cách giải quyết cũ. Tôi chắc chắn rằng chúng tôi sẽ thấy nhiều trường hợp sử dụng thú vị, phức tạp và sáng tạo hơn, đặc biệt là khi hỗ trợ trình duyệt đến với Chrome và Edge.

Dấu thời gian:

Thêm từ Thủ thuật CSS