确定比例
根据爻宽度来确定比例关系
div.outer-container {
border: 1px solid orange;
border-radius: 0.5em;
width: fit-content;
div.inner-container {
--golden-ratio: 0.618;
--yao-width: 200px;
--container-height: calc(var(--yao-width) / var(--golden-ratio));
--gap-value: calc(var(--container-height) / 11 * var(--golden-ratio));
--yao-height: calc((var(--container-height) - var(--gap-value) * 5) / 6);
border: 1px solid none;
margin: calc(var(--yao-height) * var(--golden-ratio));
width: var(--yao-width);
height: var(--container-height);
display: grid;
gap: var(--gap-value);
div.yang-yao {
background-color: #333;
border: 1px solid gray;
}
}
}
这种从内到外来配置的好处是设置相对容易,但不足之处是不能直观地得到外容器的宽高值。当需要整体排版64个卦象时,版面不好控制。
此外,整个卦象的高度似乎有点过高了,比例有点失调。
根据外容器宽度确定比例关系
第一步,确定外容器的比例大小。
div.outer-container {
border: 1px solid orange;
background: repeating-linear-gradient(45deg, #333333 0, #333333 5%, #4f4f4f 0, #4f4f4f 50%) 0 / 10px 10px;
box-sizing: border-box;
border-radius: 0.5em;
width: 200px;
aspect-ratio: 3 / 4;
}
let outerContainer = document.querySelector('.outer-container');
const {width, height} = getComputedStyle(outerContainer);
pc.log('width: %s', width);
pc.log('height: %s', height);
外容器的宽高比为3 : 4,也即手机竖屏宽高比9 : 16,在视觉上远比黄金分割率0.618 : 1好看。因此,并非任意地方设置黄金分割率都好看,而应将它用在该用的地方。
因为需要同时摆放多达64个卦像,我们希望不管内部内容如何设置,均不要自动向外扩展而影响所占用的总共空间。因此将其box-sizing属性值设置为border-box,则所占用的总共空间被牢牢固定,内边距及边框的内容自动往里设置。
背景颜色为灰底斜纹,边框颜色为橙色。
上面运行结果,左边绘制图形,右边打印外容器的最终宽高值,以防止出现因设置某些CSS属性值后、悄悄地改变了外容器的大小的意外情况出现。
第二步,设置容器子元素。
div.outer-container {
border: 1px solid orange;
background: repeating-linear-gradient(45deg, #333333 0, #333333 5%, #4f4f4f 0, #4f4f4f 50%) 0 / 10px 10px;
box-sizing: border-box;
border-radius: 0.5em;
width: 200px;
aspect-ratio: 3 / 4;
div.inner-container {
border: 1px solid red;
margin: 1em;
div.yang-yao {
background-color: hsl(280, 30%, 30%);
border: 1px solid gray;
}
}
}
let outerContainer = document.querySelector('.outer-container');
const {width, height} = getComputedStyle(outerContainer);
pc.log('width: %s', width);
pc.log('height: %s', height);
将inner-container的margin值设置为1em,则左、上、右边距按指定值设置。且各个爻只有一个边框的高度值,从上往下排列。整个inner-container的内容均在红色边框内排列。
outer-container的高度值是固定的,而inner-container的高度值也是固定且又太小,此时既不能缩小外容器的高度,又不能拉伸内容器的高度以满足margin-bottom的要求,因此下边距只好出现过多的空间。
第三步,将外容器设置为Grid,则可由Grid 容器来满足下边距的要求。
div.outer-container {
border: 1px solid orange;
background: repeating-linear-gradient(45deg, #333333 0, #333333 5%, #4f4f4f 0, #4f4f4f 50%) 0 / 10px 10px;
box-sizing: border-box;
border-radius: 0.5em;
width: 200px;
aspect-ratio: 3 / 4;
display: grid;
div.inner-container {
border: 1px solid red;
background-color: #444;
margin: 1em;
div.yang-yao {
background-color: hsl(280, 30%, 30%);
border: 1px solid gray;
}
}
}
let outerContainer = document.querySelector('.outer-container');
const {width, height} = getComputedStyle(outerContainer);
pc.log('width: %s', width);
pc.log('height: %s', height);
子容器的红色边框自动向下拉伸,而子容器的内容在其范围内按流布局排列。
第四步,将子容器也设置为Grid,以让子容器的子元素的内容自动填满子容器的空间。
div.outer-container {
border: 1px solid orange;
background: repeating-linear-gradient(45deg, #333333 0, #333333 5%, #4f4f4f 0, #4f4f4f 50%) 0 / 10px 10px;
box-sizing: border-box;
border-radius: 0.5em;
width: 200px;
aspect-ratio: 3 / 4;
display: grid;
div.inner-container {
border: 1px solid red;
background-color: #444;
margin: 1em;
display: grid;
div.yang-yao {
background-color: hsl(280, 30%, 30%);
border: 0.1px solid gray;
}
}
}
let outerContainer = document.querySelector('.outer-container');
const {width, height} = getComputedStyle(outerContainer);
pc.log('width: %s', width);
pc.log('height: %s', height);
第五步,设置子容器的gap属性值,以在子容器的子元素之间添加间距。
div.outer-container {
border: 1px solid orange;
background: repeating-linear-gradient(45deg, #333333 0, #333333 5%, #4f4f4f 0, #4f4f4f 50%) 0 / 10px 10px;
box-sizing: border-box;
border-radius: 0em;
width: 200px;
aspect-ratio: 3 / 4;
display: grid;
div.inner-container {
border: 1px solid red;
background-color: #333;
margin: 1em;
display: grid;
gap: 5px;
div.yang-yao {
background-color: silver;
border: 0px solid gray;
}
}
}
let outerContainer = document.querySelector('.outer-container');
const {width, height} = getComputedStyle(outerContainer);
pc.log('width: %s', width);
pc.log('height: %s', height);
上面取消了各爻的边框颜色,以灰色显示爻,以黑色表示间距,以突出爻与间距的关系。
总共有6个爻,当设置间距值后,在每个爻之间插入所指定值的间距,因此共插入5个间距。则每个爻的高度值为红色边框的高度值减去5个间距的高度值后,再除以6。逐渐加大gap的值,则爻的高度值将随之变小。
第六步,使用CSS变量完成最后的步骤。
div.outer-container {
--container-width: 200px;
--container-aspect-ratio: calc(3 / 4);
--container-height: calc(var(--container-width) / var(--container-aspect-ratio));
--margin-value: calc(var(--container-width) / (var(--container-aspect-ratio) * 10));
--gap-value: calc((var(--container-height) - var(--margin-value) * 2) / 11 * var(--container-aspect-ratio));
border: 1px solid orange;
box-sizing: border-box;
border-radius: 0em;
width: var(--container-width);
aspect-ratio: var(--container-aspect-ratio);
display: grid;
div.inner-container {
border: 1px solid none;
background-color: #333;
margin: var(--margin-value);
display: grid;
gap: var(--gap-value);
div.yang-yao {
background-color: #888;
border: 0px solid gray;
}
}
}
let outerContainer = document.querySelector('.outer-container');
const {width, height} = getComputedStyle(outerContainer);
pc.log('width: %s', width);
pc.log('height: %s', height);
绘制阴爻
要使用CSS绘制一个阴爻,有多种方法。
第一种方法是使用下面的HTML代码:
但HTML代码不够优雅。我们希望像阳爻一样,阴爻也在一个div上直接呈现:
第二种方法是使用clip-path,但目前各个绘制基本图形函数不支持在一个clip-path中同时绘制多个基本图形;path()函数可以同时绘制多个子路径,但又不支持百分比的形式;shape()函数支持百分比,但目前又不支持同时绘制两个子路径。
第三种方法是使用线性渐变作为蒙板。
div.outer-container {
--container-width: 200px;
--container-aspect-ratio: calc(3 / 4);
--container-height: calc(var(--container-width) / var(--container-aspect-ratio));
--margin-value: calc(var(--container-width) / (var(--container-aspect-ratio) * 10));
--gap-value: calc((var(--container-height) - var(--margin-value) * 2) / 11 * var(--container-aspect-ratio));
border: 1px solid orange;
box-sizing: border-box;
border-radius: 0em;
width: var(--container-width);
aspect-ratio: var(--container-aspect-ratio);
display: grid;
div.inner-container {
border: 1px solid none;
background-color: #333;
margin: var(--margin-value);
display: grid;
gap: var(--gap-value);
div.yang-yao,
div.yin-yao {
background-color: #888;
border: 0px solid white;
}
div.yin-yao {
mask: linear-gradient(90deg, #000 0%, #000 40%, transparent 40%, transparent 60%, #000 60%, #000 100%);
}
}
}
let outerContainer = document.querySelector('.outer-container');
const {width, height} = getComputedStyle(outerContainer);
pc.log('width: %s', width);
pc.log('height: %s', height);
但如果给各个爻加上一个边框颜色,则会发现阴爻中间断开的部分缺失边框颜色。
div.outer-container {
--container-width: 200px;
--container-aspect-ratio: calc(3 / 4);
--container-height: calc(var(--container-width) / var(--container-aspect-ratio));
--margin-value: calc(var(--container-width) / (var(--container-aspect-ratio) * 10));
--gap-value: calc((var(--container-height) - var(--margin-value) * 2) / 11 * var(--container-aspect-ratio));
border: 1px solid orange;
box-sizing: border-box;
border-radius: 0em;
width: var(--container-width);
aspect-ratio: var(--container-aspect-ratio);
display: grid;
div.inner-container {
border: 1px solid none;
background-color: #333;
margin: var(--margin-value);
display: grid;
gap: var(--gap-value);
div.yang-yao,
div.yin-yao {
background-color: #888;
border: 1px solid white;
}
div.yin-yao {
mask: linear-gradient(90deg, #000 0%, #000 40%, transparent 40%, transparent 60%, #000 60%, #000 100%);
}
}
}
let outerContainer = document.querySelector('.outer-container');
const {width, height} = getComputedStyle(outerContainer);
pc.log('width: %s', width);
pc.log('height: %s', height);
第四种方法是使用伪类来分别设置阴爻的两个实心部分:
div.outer-container {
--container-width: 200px;
--container-aspect-ratio: calc(3 / 4);
--container-height: calc(var(--container-width) / var(--container-aspect-ratio));
--margin-value: calc(var(--container-width) / (var(--container-aspect-ratio) * 10));
--gap-value: calc((var(--container-height) - var(--margin-value) * 2) / 11 * var(--container-aspect-ratio));
border: 1px solid orange;
box-sizing: border-box;
border-radius: 0em;
width: var(--container-width);
aspect-ratio: var(--container-aspect-ratio);
display: grid;
div.inner-container {
border: 1px solid none;
background-color: #333;
margin: var(--margin-value);
display: grid;
gap: var(--gap-value);
div.yang-yao,
div.yin-yao::before,
div.yin-yao::after {
background-color: #666;
border: 1px solid silver;
}
div.yin-yao {
position: relative;
&::before,
&::after {
content: '';
position: absolute;
top: 0;
width: 40%;
height: 100%;
}
&::before {
left: 0;
}
&::after {
right: 0;
}
}
}
}
let outerContainer = document.querySelector('.outer-container');
const {width, height} = getComputedStyle(outerContainer);
pc.log('width: %s', width);
pc.log('height: %s', height);
这种方式逻辑简单、易于理解、容易实现。并且,若要改变阴爻实体间距,只需简单地改变::before及::after伪类中的width属性值即可。试将该属性值改为35%或45%,通过对比以确定最喜欢的效果。
带文本标签
为方便整体排版,特将文本标签也放置于外容器中,受外容器的宽高值约束。
div.outer-container {
--container-width: 200px;
--container-aspect-ratio: calc(3 / 4);
--container-height: calc(var(--container-width) / var(--container-aspect-ratio));
--margin-value: calc(var(--container-width) / (var(--container-aspect-ratio) * 10));
--gap-value: calc((var(--container-height) - var(--margin-value) * 2) / 11 * var(--container-aspect-ratio));
border: 1px solid orange;
box-sizing: border-box;
border-radius: 0em;
width: var(--container-width);
aspect-ratio: var(--container-aspect-ratio);
display: grid;
grid-template-rows: 1fr auto;
div.inner-container {
border: 0px solid green;
background-color: #333;
margin: var(--margin-value);
display: grid;
gap: var(--gap-value);
div.yang-yao,
div.yin-yao::before,
div.yin-yao::after {
background-color: #666;
border: 1px solid silver;
}
div.yin-yao {
position: relative;
&::before,
&::after {
content: '';
position: absolute;
top: 0;
width: 40%;
height: 100%;
}
&::before {
left: 0;
}
&::after {
right: 0;
}
}
}
> p {
border: 0px solid gray;
text-align: center;
margin-top: -0.5em;
margin-bottom: 1em;
font-size: calc(var(--gap-value) * 1.2);
}
}
将文本大小设置为受--gap-value影响,则文本大小可随外容器的宽度值变化而自动按比例调节。倍数设为1.2,可适当解决当外容器过小时因文本太小而看不清的问题。
与不带文本标签的卦并排以相互比较:
body {
display: flex;
gap: 1em;
}
div.六十四卦 {
--container-width: 200px;
--container-aspect-ratio: calc(3 / 4);
--container-height: calc(var(--container-width) / var(--container-aspect-ratio));
--margin-value: calc(var(--container-width) / (var(--container-aspect-ratio) * 10));
--gap-value: calc((var(--container-height) - var(--margin-value) * 2) / 11 * var(--container-aspect-ratio));
border: 1px solid orange;
box-sizing: border-box;
border-radius: 0em;
width: var(--container-width);
aspect-ratio: var(--container-aspect-ratio);
display: grid;
grid-template-rows: 1fr auto;
div.inner-container {
border: 0px solid green;
background-color: #333;
margin: var(--margin-value);
display: grid;
gap: var(--gap-value);
div.阳爻,
div.阴爻::before,
div.阴爻::after {
background-color: #888;
border: 0px solid silver;
}
div.阴爻 {
position: relative;
&::before,
&::after {
content: '';
position: absolute;
top: 0;
width: 40%;
height: 100%;
}
&::before {
left: 0;
}
&::after {
right: 0;
}
}
}
> p {
border: 0px solid gray;
text-align: center;
margin-top: -0.5em;
margin-bottom: 1em;
font-size: calc(var(--gap-value) * 1.2);
color: white;
}
}
带文本及不带文本的区别仅在于在内容器中是否多出一个p元素,而在外容器中通过设置:
则足以充分应对这两种情况。因此两种元素仅需一个CSS选择器即可。
六十四卦的整体排版
64个卦象,数量较多,因此最好使用JavaScript来自动生成各个卦象。其必要的HTML模板结构如下:
在负责排版的容器中通过引用另一个类有文本,让用户自由选择。
因为六十四卦的内容及顺序又是固定的,则HTML代码又可简化为:
剩下的任务就完全交由JavaScript代劳了。
div.六十四卦-container {
--gua-min-width: 150px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(var(--gua-min-width), 1fr));
justify-items: center;
gap: 1em;
div.六十四卦 {
--container-width: var(--gua-min-width);
--container-aspect-ratio: calc(3 / 4);
--container-height: calc(var(--container-width) / var(--container-aspect-ratio));
--margin-value: calc(var(--container-width) / (var(--container-aspect-ratio) * 10));
--gap-value: calc((var(--container-height) - var(--margin-value) * 2) / 11 * var(--container-aspect-ratio));
border: 1px solid orange;
box-sizing: border-box;
border-radius: 0em;
width: var(--container-width);
aspect-ratio: var(--container-aspect-ratio);
display: grid;
grid-template-rows: 1fr auto;
div.inner-container {
border: 0px solid green;
background-color: #333;
margin: var(--margin-value);
display: grid;
gap: var(--gap-value);
div.阳爻,
div.阴爻::before,
div.阴爻::after {
background-color: #888;
border: 0px solid silver;
}
div.阴爻 {
position: relative;
&::before,
&::after {
content: '';
position: absolute;
top: 0;
width: 40%;
height: 100%;
}
&::before {
left: 0;
}
&::after {
right: 0;
}
}
}
> p {
border: 0px solid gray;
text-align: center;
margin-top: -0.5em;
margin-bottom: 1em;
font-size: calc(var(--gap-value) * 1.2);
color: cyan;
}
}
}
import { 六十四卦Factory } from '/mhys/js/esm/BaGua.js';
let guaNames = [
'乾', '坤', '屯', '蒙', '需', '讼', '师', '比',
'小畜', '履', '泰', '否', '同人', '大有', '谦', '豫',
'随', '蛊', '临', '观', '噬嗑', '贲', '剥', '复',
'无妄', '大畜', '颐', '大过', '坎', '离', '咸', '恒',
'遁', '大壮', '晋', '明夷', '家人', '睽', '蹇', '解',
'损', '益', '夬', '姤', '萃', '升', '困', '井',
'革', '鼎', '震', '艮', '渐', '归妹', '丰', '旅',
'巽', '兑', '涣', '节', '中孚', '小过', '既济', '未济'
];
guaNames.forEach(guaName => {
六十四卦Factory.create六十四卦(guaName)
.then(gua => {
let yaos = [gua.上卦.三爻, gua.下卦.三爻].flat().map(yaoValue => {
return yaoValue ? '阳爻' : '阴爻';
});
genGuaDiv(gua.象例, yaos);
});
});
function genGuaDiv(象例, 六爻) {
let guaDiv = document.createElement('div');
guaDiv.className = '六十四卦';
let innnerContainerDiv = document.createElement('div');
innnerContainerDiv.className = 'inner-container';
六爻.forEach(yaoName => {
let yao = document.createElement('div');
yao.className = yaoName;
innnerContainerDiv.appendChild(yao);
});
guaDiv.appendChild(innnerContainerDiv);
let 六十四卦Container = document.querySelector('.六十四卦-container');
if (六十四卦Container.classList.contains('有文本')) {
let p = document.createElement('p');
p.textContent = 象例;
guaDiv.appendChild(p);
}
六十四卦Container.appendChild(guaDiv);
}
如果希望每行放8个卦,且又须保持卦的最小宽度值,则可将模板列指定为固定的列数,且若一行的水平空间不够时,需在容器上建立滚动容器:
效果如下:
div.六十四卦-container {
--gua-min-width: 120px;
display: grid;
grid-template-columns: repeat(8, minmax(var(--gua-min-width), 1fr));
justify-items: center;
overflow: scroll;
gap: 1em;
div.六十四卦 {
--container-width: var(--gua-min-width);
--container-aspect-ratio: calc(3 / 4);
--container-height: calc(var(--container-width) / var(--container-aspect-ratio));
--margin-value: calc(var(--container-width) / (var(--container-aspect-ratio) * 10));
--gap-value: calc((var(--container-height) - var(--margin-value) * 2) / 11 * var(--container-aspect-ratio));
border: 1px solid orange;
box-sizing: border-box;
border-radius: 0em;
width: var(--container-width);
aspect-ratio: var(--container-aspect-ratio);
display: grid;
grid-template-rows: 1fr auto;
div.inner-container {
border: 0px solid green;
background-color: #333;
margin: var(--margin-value);
display: grid;
gap: var(--gap-value);
div.阳爻,
div.阴爻::before,
div.阴爻::after {
background-color: #888;
border: 0px solid silver;
}
div.阴爻 {
position: relative;
&::before,
&::after {
content: '';
position: absolute;
top: 0;
width: 40%;
height: 100%;
}
&::before {
left: 0;
}
&::after {
right: 0;
}
}
}
> p {
border: 0px solid gray;
text-align: center;
margin-top: -0.5em;
margin-bottom: 1em;
font-size: calc(var(--gap-value) * 1.2);
color: cyan;
}
}
}
import { 六十四卦Factory } from '/mhys/js/esm/BaGua.js';
let guaNames = [
'乾', '坤', '屯', '蒙', '需', '讼', '师', '比',
'小畜', '履', '泰', '否', '同人', '大有', '谦', '豫',
'随', '蛊', '临', '观', '噬嗑', '贲', '剥', '复',
'无妄', '大畜', '颐', '大过', '坎', '离', '咸', '恒',
'遁', '大壮', '晋', '明夷', '家人', '睽', '蹇', '解',
'损', '益', '夬', '姤', '萃', '升', '困', '井',
'革', '鼎', '震', '艮', '渐', '归妹', '丰', '旅',
'巽', '兑', '涣', '节', '中孚', '小过', '既济', '未济'
];
guaNames.forEach(guaName => {
六十四卦Factory.create六十四卦(guaName)
.then(gua => {
let yaos = [gua.上卦.三爻, gua.下卦.三爻].flat().map(yaoValue => {
return yaoValue ? '阳爻' : '阴爻';
});
genGuaDiv(gua.象例, yaos);
});
});
function genGuaDiv(象例, 六爻) {
let guaDiv = document.createElement('div');
guaDiv.className = '六十四卦';
let innnerContainerDiv = document.createElement('div');
innnerContainerDiv.className = 'inner-container';
六爻.forEach(yaoName => {
let yao = document.createElement('div');
yao.className = yaoName;
innnerContainerDiv.appendChild(yao);
});
guaDiv.appendChild(innnerContainerDiv);
let 六十四卦Container = document.querySelector('.六十四卦-container');
if (六十四卦Container.classList.contains('有文本')) {
let p = document.createElement('p');
p.textContent = 象例;
guaDiv.appendChild(p);
}
六十四卦Container.appendChild(guaDiv);
}