马黑黑 发表于 2024-8-31 20:22
手机里的也是古董的
那就得卸了重新装了
绿叶清舟 发表于 2024-8-31 20:26
那就得卸了重新装了
留着做个纪念。加装一个Edge什么的
<style>
.artbox { position: relative; }
.artbox > p { position: relative; margin: 10px 0; font: normal 18px / 26px sans-serif; }
.artbox mark { color: black; background: lightblue; padding: 2px 4px; }
.textRed { color: red; }
.textMid { text-align: center; }
.showDiv { position: relative; }
</style>
<div class="artbox">
<h2 class="textMid">第二十六讲:学一点点JS(六)JS操作CSS变量</h2>
<p>CSS变量是自定义的CSS属性,它有一个高大上的名称叫级联变量,称之为CSS变量更为通俗易懂。CSS变量主要用来解决两个层面的问题,一是基于CSS代码。考虑一下如下情形:当一大堆的属性设置都会用到相同的值,我们就可以给它们设置一个变量值,各属性通过变量名使用此变量值,日后想修改这一系列属性值时,只需修改被引用的变量值,非常方便。再考虑一下另一种情形:通过作用于特定属性的若干个变量来构建一种页面风格,可以同时创建几个风格,风格切换时只需仅需改变变量名,是不是也很高效?二是基于JS,这是本讲要讨论的课题,当然,正式探讨它之前,我们先来认真梳理一下CSS变量。</p>
<p>任何CSS属性的值都可以使用CSS变量来表示。CSS变量分两个步骤实现:第一步是声明变量被给变量赋值,格式为 <mark>--变量名: 变量值</mark>,双连接符 <mark>--</mark> 是变量名的前缀,其后紧跟一个变量名称,加上小角冒号之后就可以给变量名进行赋值,例如,<mark>--color: red;</mark>,这里,<mark>--color</mark> 是变量名,相当于一个CSS属性,实际上就是一个自定义的CSS属性,赋值方法和其他CSS属性的做法一样,都是用键值对的方式完成,完了需要用小角分号 <mark>;</mark> 收尾;第二步是在需要用到CSS变量值的属性中引用变量,使用关键字 <mark>var</mark> 来完成,格式为 <mark>属性名: var(--变量名);</mark>,例如,<mark>color: var(--color);</mark> 表示前景色 <span class="textRed">color</span> 属性使用变量 <span class="textRed">--color</span> 定义的颜色值。以下示例,通过 <span class="textRed">--color</span> 和 <span class="textRed">--bg</span> 声明前景色和背景色变量,再在 <span class="textRed">color</span> 和 <span class="textRed">background</span> 属性中分别使用两个变量赋值:</p>
<div class="hE"><pre id="pre1">
<style>
.cDiv1 {
--color: green;
--bg: lightgray;
width: 400px;
height: 80px;
color: var(--color);
background: var(--bg);
padding: 10px;
}
</style>
<div class="cDiv1">通过CSS变量设置元素的前景色和背景色</div>
</pre></div>
<p><button id="btn1" type="button" value="1">点击查看效果</button></p>
<div id="sbox1" class="showDiv"></div>
<p>这样,动态修改盒子的前景色、背景色时,我们可以不用去一一操作元素的对应CSS属性,只需改变对应的CSS变量值就能达到目的,这对大批量动态修改元素的特定属性值特别有用。下面的代码在上述代码基础上使用JS来动态操作盒子的前景色和背景色两个CSS变量,为了便于操作,CSS使用id选择器、盒子使用id标识属性:</p>
<div class="hE"><pre id="pre2">
<style>
#cDiv2 {
--color: green;
--bg: lightgray;
width: 400px;
height: 80px;
color: var(--color);
background: var(--bg);
padding: 10px;
}
</style>
<div id="cDiv2">点击本div随机改变前景色和背景色</div>
<script>
/* 函数 :生成两个hsl颜色 */
create2HueColors = () => {
var a = Math.round(Math.random() * 360); /* 变量 a : 获取 0~360 之间的随机整数 */
var b = (a + 150) % 360; /* 变量 b :与 a 拉开150度的距离(取360的余数保证hue合理) */
/* 返回存储两个hsl颜色的对象 */
return {
c1: `hsl(${a}, 100%, 50%)`,
c2: `hsl(${b}, 100%, 50%)`,
};
};
/* div盒子点击事件 */
cDiv2.onclick = () => {
var color = create2HueColors();
cDiv2.style.setProperty('--color', color.c1);
cDiv2.style.setProperty('--bg', color.c2);
};
</script>
</pre></div>
<p><button id="btn2" type="button" value="2">点击查看效果</button></p>
<div id="sbox2" class="showDiv"></div>
<p>代码中的函数 create2HueColors() 生成两个色域范围不沾边的色相值以便让前景色和背景色拉开区间,该函数返回一个对象,对象里记录所生成的两组 hsl 颜色,记作 c1 和 c2。div的点击事件是我们要看的重点:首先声明一个变量 color,通过调用函数 create2HueColors() 给它赋值;接着,使用JS内置的 <span class="textRed">setProperty()</span> 方法改变CSS属性,要改变的是CSS变量 --color 和 --bg,它们的值都是从JS变量 color 中获取,一个是 color.c1,另一个是 color.c2,这是调用事先编写好的生成两个色相值的函数 create2HueColors() 所返回的值。<span class="textRed">setProperty()</span> 方法我们在上一讲中介绍过,它用来给CSS属性赋值,CSS变量本质上是一个自定义的CSS属性,所以自然而然可以使用它来改变变量值。</p>
<p>我们还可以为上例既有CSS属性如宽高等的属性值设置CSS变量,一切凭需要进行设计。做音画帖最需要的是操纵CSS关键帧动画,当有较多的元素都运行不同的关键帧动画,如何统一管理这些动画的运行与暂停就是我们必须解决的问题,这个时候,CSS变量就能派上用场,我们可以设置一个 --state 变量,用以表示动画运行属性即 animation 属性的运行(running)或暂停(paused)这两种状态,然后根据 --state 属性的上一个属性值来决定下一个属性值,两个值来回轮换,以此达成操控动画的播放与暂停。试看如下例子,演示后可点击div切换动画运行状态:</p>
<div class="hE"><pre id="pre3">
<style>
#cDiv3 {
--state: running;
margin: 30px;
width: 60px;
height: 60px;
background: olive;
animation: rot 6s linear infinite var(--state);
}
@keyframes rot { to { transform: rotate(360deg); } }
</style>
<div id="cDiv3" title="点击切换动画状态"></div>
<script>
/* div盒子点击事件 */
cDiv3.onclick = () => {
var currentState = window.getComputedStyle(cDiv3).getPropertyValue('--state');
cDiv3.style.setProperty('--state', currentState === 'running' ? 'paused' : 'running');
};
</script>
</pre></div>
<p><button id="btn3" type="button" value="3">点击查看效果</button></p>
<div id="sbox3" class="showDiv"></div>
<p>CSS代码的重点是,#cDiv3 选择器通过 animation 属性运行关键帧动画 rot,并使用 --state 变量来控制运行状态,--state 的初始值是 running,这意味着动画一开始就运行。JS代码共两行,第一行是获取当前的 --state 变量值,使用的是JS内置的API接口 <mark>window.getComputedStyle(元素)</mark>,它会返回一系列的值,我们仅取其CSS属性值 <mark>getPropertyValue(属性)</mark>,示例要操作的元素是 cDiv3,要获取的属性值是自定义CSS变量属性 --state;第二行,用 setProperty() 方法给 --state 变量赋值,赋值过程使用三元运算判断当前动画运行状态 currentState 的值,若为 running,则令其改为 paused,反之,令其改为 running,这样,单击元素就可以让div在动画运行状态与暂停状态间来回切换。 </p>
<p>通过 <mark>window.getComputedStyle(元素).getPropertyValue(属性)</mark> 方法获得指定元素的指定属性的值是个聪明的做法,不过在音画帖中,我们通过音频基于JS的 paused 暂停属性来决定关键帧动画、视频等的播放或暂停,这在前面的章节中介绍过,这里再详细讲一讲。以下示例,在上一个示例的基础上,① 加入音频标签,并通过音频标签的 <span class="textRed">oncanplay</span> 可以播放、<span class="textRed">onplaying</span> 正在开始播放、<span class="textRed">onpause</span> 暂停这三个事件一同运行一个预设的自定义函数,以此来管控CSS关键帧动画的运行与暂停所依托的CSS变量 --state 的属性值;② 以div元素的伪元素 ::before 模拟播放器,剥夺宿主元素的点击交互功能,将点击操作权限交由伪元素接管。试看代码和演示效果:</p>
<div class="hE"><pre id="pre4">
<style>
#cDiv4 {
--state: running;
margin: 30px;
width: 400px;
height: 200px;
border: 1px solid gray;
pointer-events: none; /* 禁用指针交互事件 */
position: relative;
}
#cDiv4::before {
content: '';
position: absolute;
left: 30px;
top: 30px;
width: 60px;
height: 60px;
background: olive;
cursor: pointer; /* 鼠标指针 :手型 */
pointer-events: auto; /* 接受指针交互事件 */
animation: rot 6s linear infinite var(--state);
}
@keyframes rot { to { transform: rotate(360deg); } }
</style>
<div id="cDiv4" title="播放/暂停">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=1446035057" autoplay loop></audio>
</div>
<script>
//联动控制函数 mState
mState = () => {
//设置基于cDiv4的 --state 变量值 :如果音频暂停值为 'paused',反之值为 'running'
cDiv4.style.setProperty('--state', aud.paused ? 'paused' : 'running');
//设置cDiv4的弹出式标题文本 :用布尔变量变数字去读取数组,后续解释
cDiv4.title = ['暂停','播放'][+aud.paused];
//...这里可以加入对视频的控制
};
aud.oncanplay = () => mState(); //音频可以播放时运行联动函数
aud.onplaying = () => mState(); //音频正在开始播放时运行联动函数
aud.onpause = () => mState(); //音频暂停时运行联动函数
//以上三个监听事件可以和在一行写,如下:
//aud.oncanplay = aud.onplaying = aud.onpause = () => mState();
//cDiv4点击事件 :如果音频暂停则播放它,反之暂停它
cDiv4.onclick = () => aud.paused ? aud.play() : aud.pause();
</script>
</pre></div>
<p><button id="btn4" type="button" value="4">点击查看效果</button></p>
<div id="sbox4" class="showDiv"></div>
<p>代码解释:</p>
<p>(一)CSS代码里,pointer-events 属性是指针交互事件属性,值为 <span class="textRed">none</span> 表示禁用、为 <span class="textRed">auto</span> 表示可用,这样,宿主元素 cDiv4 不接受指针交互,其下子元素会继承这个属性设置,也都不接受指针交互,包括title、指针样式都会失效,因此,作为子元素的伪元素要设置为接受才具备接受点击等交互操作功能。</p>
<p>(二)JS代码中,伪元素的 <span class="textRed">title</span> 属性值通过宿主元素 cDiv4 传递,尽管宿主元素因为指针交互功能受禁;CSS变量 --state 属性也是通过宿主元素传递。这里请特别注意,父元素的CSS变量可以传递给子元素,前提是子元素不要设置变量值、只能引用变量。上述CSS代码,伪元素的 animation 属性引用了 --state 变量,<span class="textRed">var(--state)</span>,但没有 <mark>--state: running/paused</mark> 这样的赋值,声明与赋值只能有父元素完成,否则就无法通过父元素给子元素传递变量值,JS对 --state 属性的操作就作用不到子元素。</p>
<p>(三)说一下数组在联动函数中的应用:<mark>cDiv4.title = ['暂停','播放'][+aud.paused];</mark> 这一句,<mark>['暂停','播放']</mark>是一个数组,共两个数组元素,而 <mark>[+aud.paused]</mark> 是读取数组的下标,其值为 <span class="textRed">+aud.paused</span>,aud.paused 是 audio 标签的暂停属性,暂停时为真否则为假,是布尔值,布尔值前面加一个加号 <mark>+</mark> 变为数值,<mark>true</mark> 等于1,<mark>false</mark> 等于0,刚好可以对应我们所设置的数组 <mark>['暂停','播放']</mark>,因此弹出式显示出来的title文本符合我们的要求;该句代码也可以使用三元运算语句改写:<mark>cDiv4.title = aud.paused ? '播放' : '暂停';</mark>。</p>
<p><span class="textRed">作业</span>:参照本讲最后一个示例,制作一个简单的帖子。要求:① 帖子有背景图、有视频,其中视频设置为圆形、不覆盖整个帖子;② 用伪元素 ::before 和 ::after 做两个音频播放控制器,各自有自己的背景图案,点击任意一个都可以联动控制音乐、CSS动画和视频。</p>
<p><a href="https://www.huachaowang.com/forum.php?mod=viewthread&tid=77307">返回目录</a></p>
</div>
<script>
var sc = document.createElement('script');
sc.chartset = 'utf-8';
sc.src = 'https://638183.freep.cn/638183/web/js2024/helight.js';
document.body.appendChild(sc);
var runCodes = (str,ele) => {
let reg = /(<script(.*?)>)(.|\n)*?(<\/script>)/g;
let js_str, html_str;
if(str.match(reg) !== null) {
js_str = str.match(reg);
html_str = str.replace(js_str, '').trim();
js_str = js_str.replace(/<[\/]{0,1}script[^>]*>/g,'').trim();
} else {
js_str = '';
html_str = str.trim();
}
ele.innerHTML = html_str;
let myfunc = new Function(js_str);
myfunc();
};
.forEach((btn,key) => {
var pres = ,
outs = ;
btn.onclick = () => {
runCodes(pres.innerText, outs);
btn.disabled = true;
}
});
</script>
讲义已经写到第29讲
大家好像没有学习热情,似乎是课程有些难度呢,还是我讲的不好?
马黑黑 发表于 2024-8-31 20:52
留着做个纪念。加装一个Edge什么的
那不占地方嘛,去搜来装个
这么快已经二十六了啊,申请留级行不
绿叶清舟 发表于 2024-8-31 21:13
这么快已经二十六了啊,申请留级行不
也不是不行,现在八九十岁的人参加高考也是很多的
绿叶清舟 发表于 2024-8-31 21:12
那不占地方嘛,去搜来装个
商店里有的是
马黑黑 发表于 2024-8-31 21:15
也不是不行,现在八九十岁的人参加高考也是很多的
这个制度真是,也奇怪有人一考就是十几次的也不知道咋想的
绿叶清舟 发表于 2024-8-31 21:24
这个制度真是,也奇怪有人一考就是十几次的也不知道咋想的
想啥不重要,问题是,万一考上了,如果开家长会,怎么办
马黑黑 发表于 2024-8-31 21:16
商店里有的是
找到了
绿叶清舟 发表于 2024-8-31 21:29
找到了
装个试试
马黑黑 发表于 2024-8-31 21:28
想啥不重要,问题是,万一考上了,如果开家长会,怎么办
带个照片去
绿叶清舟 发表于 2024-8-31 21:29
带个照片去
设个灵堂呀
马黑黑 发表于 2024-8-31 21:29
装个试试
已经装上了
绿叶清舟 发表于 2024-8-31 21:33
已经装上了
试用一下,需要点时间熟悉它的操作环境
马黑黑 发表于 2024-8-31 21:30
设个灵堂呀
那得把老师吓哭了
绿叶清舟 发表于 2024-8-31 21:36
那得把老师吓哭了
那也不至于,大学老师可不是都是厦大的
马黑黑 发表于 2024-8-31 21:34
试用一下,需要点时间熟悉它的操作环境
现在就在手机上了,就是字有点小了