最後一篇,雖然已經過30天了,我來加碼我最喜歡的CSS
個人CSS非常爛,CSS最讓人討厭的是各別語法都很簡單,難在組合
只好努力惡補囉@@~
https://ithelp.ithome.com.tw/users/20112550/ironman/2623
https://www.youtube.com/watch?v=rwTMBmnIHcY&list=PLqivELodHt3hxeuLX8PYaI8u1GcDaBoJo
參考書:
CSS Grid在本書的第13章-網格排版
建議先啃一啃無聊的基礎(網路文章較片段)
重構
就會想買@@~學習SOP
css或scss專案
(學習專案架構)切版
影片,是否看得懂線上教學課程的部分,我有買udemy
Jonas Schmedtmann老師的
Advanced CSS and Sass: Flexbox, Grid, Animations and More!
有帶3個專案,過程中還會重構,不過英文不好聽不懂老師的講解會很辛苦(看圖google)
小弟英文不好,打算上面1~4學好後再來挑戰
最後…
既然 CSS Grid 可以出現在 ng conf 2019,代表應該是值得學習吧
Angular and CSS Grid: Get ready to fall in love | Bill Odom
https://www.youtube.com/watch?v=lh6n0JxXD_g&list=PLOETEcp3DkCpimylVKTDe968yNmNIajlR&index=36
原始碼
https://github.com/wnodom/spacewalk
ng serve後,開啟
http://localhost:4200/menu
才會到選單,可以一項一項看,重點在css檔
imageItems: Observable<SpaceImage[]>;
/* 定義在space-imageBaseUrl.service.ts
export interface SpaceImage {
label: string;
description: string;
infoUrl: string;
imageFilename: string;
imageUrl?: string;
imageThumbnailUrl?: string;
}
space-images 的資料寫死在 space-images-data.ts
因為 SpaceImagesService 有使用的 RxJS 寫法
如果把靜態資料改寫到資料庫應該有使用價值
SpaceImage資料型別 對應到 table欄位
圖片 放在 assets/space
Service 寫在 space-images.service.ts (含SpaceImage資料型別的定義)
SpaceImage的資料 寫在 space-images-data.ts
再來開始看專案的每一選項
http://localhost:4200/example-centering
<img src="/assets/misc/satellite-emoji.png">
CSS
:host {
display: grid; // 重點是這一行,使用css grid
height: 100vh; // 100% viewport hight
place-items: center;
background-size: contain;
background-image:
radial-gradient(circle at 100%, #000, #333 65%, #eee 75%, transparent 75%),
url("/assets/misc/starfield.png")
;
}
http://localhost:4200/example-blocks
HTML
<div *ngFor="let label of labels" [title]="label">
{{ label }}
</div>
<!--
labels: string[] = [];
for (let i = 1; i < 10; i++) {
this.labels.push('' + i); // 1~9 的blocks
}
-->
CSS
:host {
padding: 20px;
display: grid;
// https://developer.mozilla.org/zh-TW/docs/Web/CSS/CSS_Grid_Layout/Basic_Concepts_of_Grid_Layout
// 格線軌道(Grid Track)
// 定義行、列
// fr 單位
// 格線軌道可以使用任何單位定義,不過格線引入了額外的單位,以助於建立有彈性的格線軌道
// 新的單位 fr 代表格線容器內,可用空間的分塊(fraction)
grid-template-columns: repeat(5, 100px);
vvvvvvvvvvvvvvvv
grid-template-rows: repeat(5, 100px);
^^^^^ 可以用任何單位
// repeat(5, 100px); 等於
// grid-template-columns: 100px 100px 100px 100px 100px;
grid-gap: 20px;
^^^^^^^^
// 每個grid的間隔
// 可以拆成grid-column-gap、grid-row-gap properties
}
div {
// 置中 label
display: grid;
place-content: center;
font-family: sans-serif;
font-size: 200%;
border: 3px solid darkgreen;
border-radius: 10px;
background-color: rgba(0, 100, 0, 0.25);
}
Bill Odom 特別強調
grid
is not an HTML constructgrid
doesn't really exist in HTML可以用開發者工具看Elements
(所以Angular Elements不等於HTML?)
<!--可以看到grid的layout-->
<example-blocks _nghost-dhy-c1></example-blocks>
http://localhost:4200/example-sandbox
github上的sandbox跟demo的程式有不少差異
HTML
<div class="sandbox">
<header>
<h1>Pale Blue Dot</h1>
</header>
<main>
<p>
Consider...
</p>
<p>
The Earth ...
</p>
<p>
Our posturings, ...
</p>
<p>
The Earth ...
</p>
<p>
It has been said that ...
</p>
</main>
<img class="carl" src="/assets/misc/Carl_Sagan_-_1980.jpg">
<img class="pbd" src="/assets/misc/Pale_Blue_Dot.png">
<aside>
<h2>
If we crave some cosmic purpose, then let us find
ourselves a worthy goal.
</h2>
</aside>
<footer>
<h3>
Text from
<em>Pale Blue Dot: A Vision of the Human Future in Space</em>
by Carl Sagan (1994)
</h3>
</footer>
</div>
CSS
/* See comments at the bottom for changes to try. */
// sandbox 裡面的,背景淺藍
.sandbox {
display:grid;
grid-gap:50px; // 每個grid相距50px
// 變成2欄,欄寬分別是500px 800px
grid-template-columns:500px 800px
background-color: lightblue;
padding: 20px;
}
// sanbox 底下的element,背景白色
.sandbox > * {
background-color: white;
padding: 20px;
}
img {
width: 100%;
}
header {
font-size:200%;
font-weight:bold;
grid-column: span 2; // 合併2欄的空間
}
aside {
}
main {
}
.carl {
// 移到第1欄位(會導致其他grid位置順序改變)
grid-column-start: 1;
grid-row-stat: 1; // 移到第1列
width: 100%;
}
.pbd {
}
footer {
grid-column: span 2; // 合併2欄的空間
}
/*
.sandbox {
display: grid;
grid-gap: 20px;
grid-template:
" header header header "
" aside main carl "
" aside main pbd "
" footer footer footer "
/ 200px 2fr 1fr
;
background-color: lightblue;
padding: 20px;
}
@media (max-width: 600px) {
.sandbox {
grid-template:
" carl "
" header "
" aside "
" main "
" pbd "
" footer "
;
}
}
.sandbox > * {
background-color: white;
padding: 20px;
}
img {
width: 100%;
}
header { grid-area: header; }
aside { grid-area: aside; }
main { grid-area: main; }
.carl { grid-area: carl; }
.pbd { grid-area: pbd; }
footer { grid-area: footer; }
*/
github那個專案裡沒有,我手打出來
HTML
VVVVV label值會是1~5,可在css下title="3"
<div *ngFor="let label of labels" [title]="label">
^^^^^^^^^^^^^^ property
{{ label }}
</div>
TS
...
labels: string[] = [];
constructor(){
for(let i=1; i<5; i++){
this.labels.push(''+i);
}
}
CSS
:host{
display: grid;
padding: 20px;
grid-template-columns: repeat(3, 200px);
grid-gap: 20px;
}
div{
display: grid;
place-content: center;
font-family: sans-serif;
font-size: 200%;
border: 3px solid darkblue;
border-radius: 10px;
background-color: rgba(0,0,100,0.25);
}
// div裡面,有property title=3的
div[title="3"]{
// 1.先確定選到了
border: 4px solid red;
background-color: lightgreen;
// 2.試玩語法
// 讓這個div跑到grid的第2列
grid-row: 2;
// 從column 1 到 last column
grid-column: 1 / -1
// -1 從尾端數回來
}
以上單一頁看起來很簡單,但若堆疊起來呢?(stack)
遇到完整專案怎麼辦?
使用grid-template-area可以比其他排板系統更省tag
這邊的程式碼不完整,
我覺得這一節較難
建議看卡司伯大大的文章
https://wcc723.github.io/css/2017/03/22/css-grid-layout/
此處影片裡的workshop-1(時間1:02:00)其實是github的example-sandbox
http://localhost:4200/example-sandbox
標題為Pale Blue Dot
.sandbox{
background-color:lightblue;
padding:20px;
display:grid;
grid-gap:50px; // 下面5個areas之間會隔50px
grid-template-areas:
" header " // 在.sandbox底下定義5個areas,而且依序顯示
" main "
" footer "
" carl "
" pbd "
// 如果改成
// " main "
// " header "
// 則 main 就會顯示在 header 前面
// 也可以寫成
" header header "
" carl main " // carl 會排在 main 左邊,即時放大也一樣
" footer footer "
" pbd pbd "
grid-template: // 不知道為何要去掉-areas
// 可以設定area的高度
" header header " 300px
" carl main " auto // 其他沒特別設定的要加auto,不然會跑板
" footer footer " auto
" pbd pbd " auto
/ 200px 500px (欄位的寬度)
// 也可以改成用fr
// fr 代表格線容器內,可用空間的分塊(fraction)
/ 200px 1fr (前面200px 後面1fr)
/ 1fr 1fr (一半一半,正確寫法)
/ 50% 50% (也是一半一半,但grid-gap會跟1fr 1fr不一樣)
;
}
header { grid-area:header; }
^^^^^^ 指定grid-template-areas:設定的區域(area)
main { grid-area: main; }
footer { grid-area: footer; }
.carl { grid-area: carl; }
.pbd { grid-area: pbd; }
aside { grid-area: aside; }
localhost:4200/example-image-gallery
TS
static label = 'Ooooh Pretty';
imageItems: Observable<SpaceImage[]>;
constructor(svc: SpaceImagesService) {
this.imageItems = svc.load('planetary-nebulae');
// const imageBaseUrl = '/assets/space';
// 去讀/assets/space/planetary-nebulae裡的jpg檔,值得看原始碼學習
HTML
<img
*ngFor="let imageItem of imageItems | async"
[src]="imageItem.imageThumbnailUrl"
[title]="imageItem.label"
>
CSS
:host {
display: grid; VVVVVV grid寬度
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
grid-auto-rows: 150px; ^^^^^^^^^ 自動補滿可用空間
grid-gap: 20px;
// vh:view height,螢幕可視範圍高度,讓圖片可以隨著螢幕大小調整
min-height: 100vh;
padding: 20px;
background-color: darkgrey;
}
img {
width: 100%;
height: 100%;
vvvvvvvvvv // tag 的 src 載入內容後,會取代原本元素
object-fit: cover;
^^^^^ // 填滿元素的寬度及高度(維持原比例),通常會剪掉部分的物件
// https://blog.camel2243.com/2017/01/21/css-object-fit-object-position-%E8%AA%BF%E6%95%B4%E7%BD%AE%E6%8F%9B%E5%85%83%E7%B4%A0img-%E7%AD%89%E7%9A%84%E5%85%A7%E5%AE%B9/
// IE、EDGE好像不支援
}
localhost:4200/example-debugging
這個範例只是展示grid可以很簡單的排板
幾個用到的語法如下:
定義 5欄4列
grid-template-columns: repeat(5, 100px);
grid-template-rows: repeat(5, 100px);
grid-gap: 20px;
grid-column: 1; // 1欄
grid-row: 1 / -1; // 第1列到最後1列
grid-row-end: -2; // 到倒數第2列
grid-column-end: -2; // 倒倒數第2欄
原始碼(沒有註解)
TS
static label = 'Good Grids Gone Bad';
labels: string[] = [];
constructor() {
for (let i = 1; i < 10; i++) {
this.labels.push('' + i);
}
}
HTML
<div *ngFor="let label of labels" [title]="label">
{{ label }}
</div>
CSS
:host {
padding: 20px;
display: grid;
min-height: 100vh;
place-content: center;
grid-template-columns: repeat(5, 100px);
grid-template-rows: repeat(5, 100px);
grid-gap: 20px;
}
div {
display: grid;
place-content: center;
font-family: sans-serif;
font-size: 200%;
border: 3px solid darkgreen;
border-radius: 10px;
background-color: rgba(0, 100, 0, 0.25);
}
[title="3"] {
background: lightblue;
grid-row: 3;
grid-column: 1 / span 3;
}
[title="4"] {
background: pink;
grid-row: span 2;
}
[title="5"] {
background: cornsilk;
grid-row-end: -1;
grid-column-end: -1;
}
[title="7"] {
grid-column: 1;
grid-row: 1 / -1;
}
/*
:host > * {
outline: 3px solid red;
outline-offset: 5px;
}
*/
[title="9"] {
border: unset;
color: transparent;
background: transparent;
grid-row-end: -2;
grid-column-end: -2;
}
label : When Worlds Collide
localhost:4200/example-image-gallery
展示 圖片 + grid排板
CSS的部分很簡單,重點在要看懂 SpaceImagesService 裡的程式
TS
imageItems: Observable<SpaceImage[]>;
/* 定義在space-imageBaseUrl.service.ts
export interface SpaceImage {
label: string;
description: string;
infoUrl: string;
imageFilename: string;
imageUrl?: string;
imageThumbnailUrl?: string;
}
space-images 的資料寫死在 space-images-data.ts
因為 SpaceImagesService 有使用的 RxJS 寫法
如果把靜態資料改寫到資料庫應該有使用價值
SpaceImage資料型別 對應到 table欄位
圖片 放在 assets/space
Service 寫在 space-images.service.ts (含SpaceImage資料型別的定義)
SpaceImage的資料 寫在 space-images-data.ts
*/
constructor(svc: SpaceImagesService) {
// assets/space/plants 裡的照片的Array
this.imageItems = svc.load('planets');
}
HTML
<img *ngFor="let imageItem of imageItems | async"
[src]="imageItem.imageThumbnailUrl"
[title]="imageItem.label" <!--用title來select-->
>
CSS
:host {
display: grid;
grid-template-columns: repeat(5, 100px);
grid-template-rows: repeat(5, 100px);
grid-gap: 20px;
align-items: start;
// 很容易會google到其他排板系統
// https://developer.mozilla.org/zh-CN/docs/Web/CSS/align-items
// start 是 Positional alignment : Pack items from the start
// The item is packed flush to each other toward the start edge of the alignment container in the appropriate axis.
padding: 20px;
background-color: cornsilk;
}
img {
width: 100%;
outline: 3px solid red;
}
[title=Mercury] {
^^^^^^^ 資料來源 space-images-data.ts
grid-row-end: -1;
grid-column-end: -1;
}
[title=Earth] {
grid-row: 4;
grid-column: 4;
z-index: 1;
}
[title=Jupiter] {
grid-row: 3 / span 2;
grid-column: span 2;
// 改成 grid-column: 3 / span 2;
// 觀察grid會從 第3欄開始 span2欄
}
[title=Saturn] {
grid-row: 2 / span 2;
grid-column: 3 / span 3;
}
localhost:4200/example-playing-cards
TS
// 四種花色
suits: string[] = [
'spades',
'hearts',
'clubs',
'diamonds',
];
ranks: string[] =
[ 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K' ];
HTML
<ng-container *ngFor="let suit of suits"> <!--花色-->
<ng-container *ngFor="let rank of ranks"> <!--A~K-->
<playing-card [rank]="rank" [suit]="suit"></playing-card>
^^^^^^^^^^^^ ^^^^ @Input ^^^^ @Input
</ng-container>
</ng-container>
CSS
:host {
display: grid;
grid-template-columns: repeat(auto-fill, 2.5in);
grid-gap: 0.25in;
padding: 0.25in;
background-color: green;
}
再來看<playing-card [rank]="rank" [suit]="suit"></playing-card>
牌的設計在另一個Component裡,樣式是用scss
看不懂,等我看得懂再回來補註解(超不負責任)
TS
import { Component, OnInit, Input } from '@angular/core';
const imageDir = '/assets/card-faces';
const pips: Record<string, string> = {
clubs: '\u2663',
diamonds: '\u2666',
hearts: '\u2665',
spades: '\u2660',
};
/* tslint:disable object-literal-key-quotes */
const rankToNumberOfPips: Record<string, number> = {
'A' : 1,
'2' : 2,
'3' : 3,
'4' : 4,
'5' : 5,
'6' : 6,
'7' : 7,
'8' : 8,
'9' : 9,
'10': 10,
'J' : 0,
'Q' : 0,
'K' : 0,
};
@Component({
selector: 'playing-card',
templateUrl: './playing-card.component.html',
styleUrls: ['./playing-card.component.scss']
})
export class PlayingCardComponent implements OnInit {
@Input() suit = '';
@Input() rank = '';
pip = '';
designPips: string[] = [];
cardImageUrl = '';
ngOnInit() {
const numPips: number = rankToNumberOfPips[this.rank];
this.pip = pips[this.suit];
if (numPips > 0) {
this.designPips = Array(numPips).fill(this.pip);
} else {
this.cardImageUrl = imageDir + '/' + this.rank + '-' + this.suit + '.png';
}
}
}
HTML
<div class="playing-card front" [ngClass]="suit">
<div class="rank">{{ rank }}</div> <!--右下角的A~K-->
<div class="corner-pip">{{ pip }}</div><!--右下角的花色-->
<div class="design" [ngClass]=" 'rank-' + rank ">
<img *ngIf="cardImageUrl" [src]="cardImageUrl">
<div *ngFor="let designPip of designPips; index as i"
[ngClass]=" 'p' + (i + 1) "
>
{{ pip }}
</div>
</div>
<div class="inverted corner-pip">{{ pip }}</div><!--右下角的花色-->
<div class="inverted rank">{{ rank }}</div><!--右下角的A~K-->
</div>
CSS
:host {
display: block; // 區塊元素
}
.playing-card {
font-family: Helvetica, sans-serif;
font-weight: bold;
font-size: 24px;
width: 2.5in;
height: 3.5in;
margin: 0;
padding: 10px;
border: 2px solid black;
border-radius: 10px;
&.front {
background-color: white;
}
display: grid;
grid-template:
" rank . . " auto
" corner-pip design . " auto
" . design . " 1fr
" . design inverted-corner-pip " auto
" . . inverted-rank " auto
/ auto 1fr auto
;
}
.rank, .corner-pip, .design {
align-self: center;
justify-self: center;
}
.rank {
grid-area: rank;
}
.corner-pip {
grid-area: corner-pip;
}
.design {
align-items: center;
grid-area: design;
display: grid;
font-size: 54px;
height: 100%;
}
.design.rank-A {
grid-template:
" p1 " 1fr
/ 1fr
;
font-size: 144px;
}
.design.rank-J,
.design.rank-Q,
.design.rank-K {
grid-template:
" p1 " 1fr
/ 1fr
;
align-items: middle;
justify-items: center;
img {
max-width: 100%;
max-height: 100%;
}
}
.design.rank-2 {
grid-template:
" . . . " 1fr
" . p1 . " 1fr
" . . . " 1fr
" . . . " 1fr
" . . . " 1fr
" . p2 . " 1fr
" . . . " 1fr
/ 1fr 1fr 1fr
;
.p2 {
@extend .inverted;
}
}
.design.rank-3 {
grid-template:
" . p1 . " 1fr
" . p2 . " 1fr
" . p3 . " 1fr
/ 1fr 1fr 1fr
;
.p3 {
@extend .inverted;
}
}
.design.rank-4 {
grid-template:
" . . . " 1fr
" p1 . p2 " 1fr
" . . . " 1fr
" . . . " 1fr
" . . . " 1fr
" p3 . p4 " 1fr
" . . . " 1fr
/ 1fr 1fr 1fr
;
.p3, .p4 {
@extend .inverted;
}
}
.design.rank-5 {
grid-template:
" . . . " 1fr
" p1 . p2 " 1fr
" . . . " 1fr
" . p3 . " 1fr
" . . . " 1fr
" p4 . p5 " 1fr
" . . . " 1fr
/ 1fr 1fr 1fr
;
.p4, .p5 {
@extend .inverted;
}
}
.design.rank-6 {
grid-template:
" . . . " 1fr
" p1 . p2 " 1fr
" . . . " 1fr
" p3 . p4 " 1fr
" . . . " 1fr
" p5 . p6 " 1fr
" . . . " 1fr
/ 1fr 1fr 1fr
;
.p5, .p6 {
@extend .inverted;
}
}
.design.rank-7 {
grid-template:
" p1 . p2 " 1fr
" p1 p3 p2 " 1fr
" p4 p3 p5 " 1fr
" p4 . p5 " 1fr
" p6 . p7 " 1fr
" p6 . p7 " 1fr
/ 1fr 1fr 1fr
;
.p6, .p7 {
@extend .inverted;
}
}
.design.rank-8 {
grid-template:
" p1 . p2 " 1fr
" p1 p3 p2 " 1fr
" p4 p3 p5 " 1fr
" p4 p7 p5 " 1fr
" p6 p7 p8 " 1fr
" p6 . p8 " 1fr
/ 1fr 1fr 1fr
;
.p6, .p7, .p8 {
@extend .inverted;
}
}
.design.rank-9 {
grid-template:
" p1 . p3 " 1fr
" p1 . p3 " 1fr
" p4 . p5 " 1fr
" p4 p2 p5 " 1fr
" p6 p2 p7 " 1fr
" p6 . p7 " 1fr
" p8 . p9 " 1fr
" p8 . p9 " 1fr
/ 1fr 1fr 1fr
;
.p6, .p7, .p8, .p9 {
@extend .inverted;
}
}
.design.rank-10 {
grid-template:
" p1 . p3 " 1fr
" p1 p2 p3 " 1fr
" p4 p2 p5 " 1fr
" p4 . p5 " 1fr
" p6 . p7 " 1fr
" p6 p8 p7 " 1fr
" p9 p8 p10 " 1fr
" p9 . p10 " 1fr
/ 1fr 1fr 1fr
;
.p6, .p7, .p8, .p9, .p10 {
@extend .inverted;
}
}
.p1 { grid-area: p1 };
.p2 { grid-area: p2 };
.p3 { grid-area: p3 };
.p4 { grid-area: p4 };
.p5 { grid-area: p5 };
.p6 { grid-area: p6 };
.p7 { grid-area: p7 };
.p8 { grid-area: p8 };
.p9 { grid-area: p9 };
.p10 { grid-area: p10 };
.p11 { grid-area: p11 };
.p12 { grid-area: p12 };
.p13 { grid-area: p13 };
.inverted.corner-pip {
grid-area: inverted-corner-pip;
}
.inverted.rank {
grid-area: inverted-rank;
}
.inverted {
transform: rotate(180deg);
}
.hearts, .diamonds {
color: rgb(212, 0, 0);
}
.spades, .clubs {
color: black;
}
localhost:4200/example-media-objects
這個範例一樣設計在一個componet裡<media-object>
TS
imageItems: Observable<SpaceImage[]>;
constructor(svc: SpaceImagesService) {
this.imageItems = svc.load('women-in-astronomy')
.pipe(
map(data => shuffleArrayInPlace([...data]))
^^^^^^^^^^^^^^^^^^^ 打亂array的元素
)
;
}
export function shuffleArrayInPlace(array: Array<any>) {
let currentIndex = array.length;
let temporaryValue: any;
let randomIndex: number;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
HTML
<media-object
*ngFor="let imageItem of imageItems | async"
[header]="imageItem.label"
[image]="imageItem.imageThumbnailUrl"
[description]="imageItem.description"
></media-object>
每一張卡片的排板
CSS
:host {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-gap: 20px;
min-height: 100%;
padding: 20px;
background-color: darkgrey;
}
用component來設計卡片
TS
@Input() header?: string;
@Input() image?: string;
@Input() description?: string;
HTML
<img [src]="image">
<header>{{ header }}</header>
<div class="description">{{ description }}</div>
CSS
:host {
display: grid;
// https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template
// 欄一 欄二 列一高
// 欄一 欄二 列二高
// 欄一寬 欄二寬
grid-template:
vvv area
" img header " auto
" img description " minmax(100px, 1fr)
/ 100px minmax(0, 1fr)
;
grid-gap: 20px;
padding: 20px;
border: 2px solid grey;
border-radius: 10px; // 卡片的圓角
background: white;
box-shadow: 5px 5px 5px 0 rgba(0, 0, 0, 0.3);
}
img {
grid-area: img;
height: 150px;
width: 100px;
object-fit: cover; //
border: 2px solid lightgrey;
}
header {
grid-area: header;
font-weight: bold;
font-size: 125%;
white-space: nowrap;
overflow: hidden; // 自動隱藏超出的文字或圖片
text-overflow: ellipsis; // 限制字數
// https://www.astralweb.com.tw/css-ellipsis/
}
.description {
grid-area: description;
overflow: hidden;
}
localhost:4200/example-full-viewport
TS
showNav = true;
imageItems: Observable<SpaceImage[]>;
constructor(svc: SpaceImagesService) {
this.imageItems = svc.load('posters');
}
HTML
<header> VVVVVVV 透過 showNav 來切換
<button (click)="showNav = !showNav" class="material-icons">
{{ showNav ? 'close' : 'menu' }}
</button>
<!--
The unusual handling of showNav ensures that the nav animations are only
triggered at the appropriate points.
-->
<a routerLink="/" (click)="showNav = showNav ? false : showNav">
<h1>{{ title }}</h1>
</a>
</header>
<nav
(click)="showNav = false"
[class.showNav]="showNav"
[class.hideNav]="showNav === false"
>
<a routerLink="/">
Home
</a>
<a routerLink="/resources">
Resources
</a>
<a routerLink="/about">
About
</a>
</nav>
<main>
<img *ngFor="let imageItem of imageItems | async"
[src]="imageItem.imageUrl"
>
</main>
<footer>
<a target="_blank" href="http://www.billodom.com/">
Bill
</a>
<a target="_blank" href="https://twitter.com/wnodom">
Twitter
</a>
<a target="_blank" href="https://github.com/wnodom">
Github
</a>
</footer>
SCSS
:host {
display: grid;
grid-gap: 0;
// 重點1
grid-template:
" header header " auto vvv grid-area
[nav-start] " main main " 1fr [nav-end] grid lines
" footer footer " auto ^^^^^^^ 命名(main的尾?)
/ [nav-start] 225px [nav-end] 1fr
^^^^^^^^^ 用中括號 稱呼網格線
;
height: 100vh;
padding: 0;
}
header {
grid-area: header;
display: grid;
grid-template:
" navToggle title " auto
/ auto minmax(0, 1fr)
;
background: lightgrey;
padding: 15px;
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.33);
z-index: 1;
// 切換nav的switch
button {
grid-area: navToggle;
outline: none;
border: none;
background-color: transparent;
text-align: left;
margin: 0;
margin-right: 15px;
padding: 0;
}
a {
grid-area: title;
}
h1 {
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
// 重點2
nav { vvv
grid-area: nav;
transform: translateX(-9999px);
opacity: 0.95;
background: lightgrey;
border-right: 3px solid darkgrey;
padding: 15px;
overflow: auto;
& > * {
display: block;
margin-bottom: 15px;
}
}
main {
grid-area: main;
display: grid;
justify-items: center;
overflow: auto;
padding: 15px;
padding-left: 50px;
img {
width: 700px;
margin: 50px;
display: block;
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.33);
}
}
footer {
grid-area: footer;
background: lightgrey;
padding: 15px;
box-shadow: 0 -5px 10px 0 rgba(0, 0, 0, 0.33);
z-index: 1;
a {
color: grey;
}
// Separate footer links with bullet characters
a:not(:last-child)::after {
padding-left: 10px;
padding-right: 10px;
content: '\2022';
}
}
header, footer, nav {
a {
text-decoration: none;
color: black;
}
}
li {
margin-top: 10px;
margin-bottom: 10px;
}
.currentSelection {
font-weight: bold;
}
vvvvvvvvvv 關鍵影格
@keyframes slideIn {
from { transform: translateX(-500px); }
to { transform: translateX(0); }
}
@keyframes slideOut {
from { transform: translateX(0); }
to { transform: translateX(-500px); }
}
顯示 Nav
.showNav { vvvvvvv key frame
animation: slideIn 500ms;
animation-fill-mode: forwards;
}
不顯示 Nav
.hideNav {
animation: slideOut 500ms;
animation-fill-mode: forwards;
}
這一集超棒的,建議對css有興趣的朋友好好研究
angular的好處是可以用component來幫我們隔離css的效果
(例如:卡牌相關範例)
前面簡單的grid還看得懂
但後來的幾個大範例就開始看不懂了
看不懂一直po程式碼很不好意思
實際去把完囉,88~~@@~~