zhangxinxu/quiz

DOM基础测试33

Opened this issue · 25 comments

本期题目如下:

大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式。

```js
// 你的JS代码写在这里
 ```

另外,务必提供在线demo,方便验证与评分。jsbin.com jsfiddle.net codepen.io或者国内类似站点都可以,也可以使用github page。

没有可访问demo积分-1分哟。

-----其他-----

因为本周在北京参加前端技术会议,周六才回上海,所以本期小测答疑直播改为6月23日周日上午10:00,预计半小时左右。直播地址:https://live.bilibili.com/21193211

首位答题者会额外2积分。

感谢您的参与!

忘记加在线demo了,补一下
demo

// 第一题
let divele = document.createElement('div')
divele.style.width = 300 + 'px'
divele.style.height = 150 + 'px'
document.body.appendChild(divele)

// 第二题
divele.style.backgroundImage = 'linear-gradient(to bottom right, red, blue)';

// 第三题 想用requestAnimationFrame 没想出怎么控制时间
let Timer = null
let colorArr = [0, 0, 255]
let changeColor = function() {
  if (colorArr[2] == 255 && colorArr[1] < 255) {
    colorArr[1] += 15
  }
  if (colorArr[1] == 255 && colorArr[2] > 0) {
    colorArr[2] -= 15
  }
  if (colorArr[2] == 0 && colorArr[1] > 0) {
    colorArr[1] -= 15
  }
  if (colorArr[2] == 0 && colorArr[1] == 0 && colorArr[0] < 255) {
    colorArr[0] += 15
  }
  if (colorArr[0] == 255) {
    clearInterval(Timer)
  }
  divele.style.backgroundImage = `linear-gradient(to bottom right, rgb(${colorArr[2]},${colorArr[1]},${colorArr[0]}), rgb(${colorArr[0]},${colorArr[1]},${colorArr[2]}))`
}
Timer = setInterval(() => {
  changeColor()
}, 1000 / 68)

> DEMO <

感觉自己第3题实现的不够优雅……

/*** 第 1 题 ***/
var box = document.createElement('div');
box.style.width = '150px';
box.style.height = '300px';
document.body.appendChild(box);

/*** 第 2 题 ***/
// 方法1:使用位置关键词
box.style.background = 'linear-gradient(to right bottom, red, blue)';

// 方法2:使用角度(纯属装B)
// 由于是长方形,所以需要用三角函数算出颜色临界线夹角(矩形右上与左下角对角线)
// var angle = Math.atan(150 / 300) * 180 / Math.PI + 90;
// box.style.background = 'linear-gradient(' + angle + 'deg, red, blue)';

/*** 第 3 题 ***/
// 思路:利用 HSL 颜色空间,H 为色相角度,所以两种颜色的渐变动画就是对 H 的递增或递减,
// 判断颜色1到颜色2的 H 跨度是否大于180度,如大于则取 360 减去 H 跨度,再取负
// 根据跨度值算出每帧渲染步长,动画过渡渲染采用 requestAnimationFrame
bgColorAnimate('hsl(0, 100%, 50%)', 'hsl(240, 100%, 50%)', 1);
function bgColorAnimate(color1, color2, duration) {
  var getHSLArr = function(hslStr) {
    var result = hslStr.match(/^hsl\((-*\d+),\s*(\d+%),\s*(\d+%)+/i);
    return [result[1], result[2], result[3]];
  };

  var getHSLStr = function(hslArr) {
    return 'hsl(' + hslArr.join(',') + ')';
  };

  var c1 = getHSLArr(color1);
  var c2 = getHSLArr(color2);
  var hue1 = c1[0];
  var hue2 = c2[0];
  var hueSpan = Math.abs(hue1 - hue2);
  
  if (hueSpan > 180) {
    if (hue1 > hue2) {
      hue1 = -(360 - hue1);
    } else {
      hue2 = -(360 - hue2);
    }
  }

  var fps = 60;  // 帧率
  var steps = Math.abs(Math.min(hue1, hue2)) / fps * duration;  // 步长

  c1[0] = hue1;
  c2[0] = hue2;

  var render = function(timestamp, dt) {
    if (dt > duration / hueSpan) {
      if (c1[0] == hue2) {
        return;
      }

      hue2 > 0 ? c1[0] += steps : c1[0] -= steps;
      hue1 > 0 ? c2[0] -= steps : c2[0] += steps;

      box.style.background = 'linear-gradient(to right bottom, ' + getHSLStr(c1) + ',' + getHSLStr(c2) + ')';
      dt = 0;
    }

    requestAnimationFrame(function(_timestamp) {
      render(_timestamp, dt + _timestamp - timestamp)
    });
  };

  requestAnimationFrame(function(timestamp) {
    render(timestamp, 0);
  });
}

Demo
https://codepen.io/crazyboy/pen/Oebpze

(function () {
	var div = document.createElement('div');
	div.style.height = '300px';
	div.style.width = '150px';
	div.style.background = 'linear-gradient(to bottom right, red , blue)';
	document.body.appendChild(div);
	var counter = function(val) {
		// 255 可以被 3 整除, 得到85帧
		var step = 3;
		var asc = true;
		return {
			next: function() {
				if (asc && val === 255) {
					asc = false;
				} else if (!asc && val === 0) {
					asc = true;
				}
				if (asc) {
					val += step;
				} else {
					val -= step;
				}
				return val;
			}
		}; 
	}
	var descendCounter = counter(255);
	var riseCounter = counter(0);
	//想在一秒内得到85帧, 约得11毫秒
	setInterval(function() {
		div.style.background = 'linear-gradient(to bottom right,rgb(' + descendCounter.next() + ',0,' + riseCounter.next() + '),rgb(' + riseCounter.next() + ',0,' + descendCounter.next() + '))';
	}, 11);
})();

demo

// 1
const div = document.createElement('div')
div.style.width = '150px'
div.style.height = '300px'
document.body.appendChild(div)

// 2
div.style.background = 'linear-gradient(to bottom right, red, blue)'

// 3 方法一
const animateFn = () => {
  div.style.background = 'linear-gradient(to bottom right, red, blue)'
  let start = null
  function step(timestamp) {
    if (!start) start = timestamp
    const progress = timestamp - start
    const color = Math.min((progress * 255) / 1000, 255).toFixed(0)
    const red = 'rgb(' + (255-color) + ',0,' + color + ')'
    const blue = 'rgb(' + color + ',0,' + (255-color) + ')'
    div.style.background = 'linear-gradient(to bottom right, ' + red +', '+ blue + ')'
    if (progress < 1000) {
      window.requestAnimationFrame(step)
    }
  }
  window.requestAnimationFrame(step)
}

animateFn()

// 方法二
const div1 = document.createElement('div')
const inner = document.createElement('div')
div1.classList.add('outer')
inner.classList.add('inner')

document.body.appendChild(div1)

const animateFn1 = () => {
  inner.style.background = 'linear-gradient(to bottom right, red, blue, red)'
  div1.appendChild(inner)

  let start = null
  function step(timestamp) {
    if (!start) start = timestamp
    const progress = timestamp - start
    const distance = Math.min((progress * 15) / 100, 150)
    inner.style.left = -1 * distance + 'px'
    inner.style.top = -1 * distance * 2 + 'px'
    if (progress < 1000) {
      window.requestAnimationFrame(step)
    } else {
      div1.removeChild(inner)
      div1.style.background = 'linear-gradient(to bottom right, blue, red)'
    }
  }
  window.requestAnimationFrame(step)
}

animateFn1()

codepen

//1
var elm = document.createElement('div')
elm.style.width = '300px'
elm.style.height = '150px'
document.body.append(elm)

function getTanDeg(tan) {
    var result = Math.atan(tan) / (Math.PI / 180)
    result = Math.round(result)
    return result
}
var result = getTanDeg(0.5)

var i = 180 - result
//2
elm.style.background = 'linear-gradient(' + i + 'deg,red,blue)'
//3
function roll() {
    if (i === 360 - 27) {
        i = 180 - 27
    }
    elm.style.background = 'linear-gradient(' + i + 'deg,red,blue)'
    // 一秒60帧 一帧加 3 deg
    i += 3
    requestAnimationFrame(roll)
}
roll()

Demo
话说查了一下IE12才支持模板字符串
hsl的渐变有点反常识


补充:参考了几种CSS渐变背景图片transtion动画方法 « 张鑫旭-鑫空间-鑫生活,尝试了houdini

var aniDiv=document.createElement('div')
aniDiv.style.height="300px"
aniDiv.style.width="150px"
aniDiv.style.background="linear-gradient(to right bottom,red,blue)"
document.body.appendChild(aniDiv)

function getBkg(max,min){
  return 'linear-gradient(to right bottom,'+max+','+min+')'
}

function aniGoRGB(){
  var start=Date.now();
  function step(){
    var min=0.255*(Date.now()-start)
    var max=255-min
    if(min<255){
      aniDiv.style.background=getBkg(
      'rgb('+max+',0,'+min+')',
      'rgb('+min+',0,'+max+')'
      )
      window.requestAnimationFrame(step)
    }else{
      max=0,min=255
      aniDiv.style.background=getBkg(
      'rgb('+max+',0,'+min+')',
      'rgb('+min+',0,'+max+')'
      )
    }
  }
  window.requestAnimationFrame(step)
}

function aniGoHex(){
  var start=Date.now();
  function step(){
    var min=parseInt(0xff/1e3*(Date.now()-start))
    var max=0xff-min
    if(max>0){
      aniDiv.style.background=getBkg(
      '#'+(((max<<16)+min).toString(16)),
      '#'+(((min<<16)+max).toString(16))
      )
      window.requestAnimationFrame(step)
    }else{
      aniDiv.style.background=getBkg(
      '#0000ff',
      '#ff0000'
      )
    }
  }
  window.requestAnimationFrame(step)
}

function aniGoHSL(){
  var start=Date.now();
  function step(){
    var delta=parseInt((360-240)/1e3*(Date.now()-start))
    var max=240+delta
    var min=360-delta
    if(max<360){
      aniDiv.style.background=getBkg(
        'hsl('+min+', 100%, 50%)',
        'hsl('+max+', 100%, 50%)'
      )
      window.requestAnimationFrame(step)
    }else{
      aniDiv.style.background=getBkg(
        'hsl(240, 100%, 50%)',
        'hsl(0, 100%, 50%)'
      )
    }
  }
  window.requestAnimationFrame(step)
}

function aniGoBkgPos(){
  aniDiv.style.transition="background-position 1s";
  aniDiv.style.background='linear-gradient(to right bottom, red, blue, red) 100% 100%/200% 200%';
  setTimeout(function(){
    aniDiv.style.background='linear-gradient(to right bottom, blue, red)';
    aniDiv.transition="";
  },1000)
}

function aniGoHoudini(){
  if(!(CSS&&CSS.registerProperty)){aniGoRGB();return;}
  aniDiv.style.background='linear-gradient(to right bottom, var(--start-stop), var(--end-stop))';
  aniDiv.style.transition="--start-stop 1s, --end-stop 1s";
  aniDiv.className="houdini";
  setTimeout(function(){
    aniDiv.style.background='linear-gradient(to right bottom, blue, red)';
    aniDiv.style.transition="";
    aniDiv.className="";
  },1000)
}
if (CSS&&CSS.registerProperty) {
    CSS.registerProperty({
        name: '--start-stop',
        syntax: '<color>',
        inherits: false,
        initialValue: 'transparent'
    });
    CSS.registerProperty({
        name: '--end-stop',
        syntax: '<color>',
        inherits: false,
        initialValue: 'transparent'
    });
}

demo

    /**
     *  1.添加div元素
     */
    let oDiv = document.createElement("div");
    oDiv.style.width = "300px";
    oDiv.style.height = "150px";
    document.body.appendChild(oDiv);
    /**
     *  2.添加渐变
     */
    oDiv.style.backgroundImage = "linear-gradient(to right bottom, red, blue)";
    /**
     *  3.渐变动画
     *  红色 255,0,0  蓝色 0 0 255
     *  红色变蓝色 r减小 b增加
     *  蓝色变红色 r增加 b减小
     */
    let red_r = 255, red_b = 0, blue_r = 0, blue_b = 255,timer;
    function changeColor(){
        red_r--;
        red_b++;
        blue_r++;
        blue_b--;
        oDiv.style.backgroundImage = `linear-gradient(to right bottom, rgb(${red_r},0,${red_b}), rgb(${blue_r},0,${blue_b}))`;
        if(red_r === 0){
            cancelAnimationFrame(timer);
        }
        timer = requestAnimationFrame(changeColor);
    }
    changeColor();

预览地址

//第一题,第二题
var box = document.createElement('div');
  box.style.width = '300px';
  box.style.height = '150px';
  box.style.background = 'linear-gradient(to bottom right, red , blue)';
  document.body.append(box);

//第三题
//[requestAnimationFrame](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame)
//根据官方文档requestAnimationFrame回调函数执行次数通常是每秒60次,通过内置函数时间戳设置1s
//红蓝对应RGB红色(255,0,0)蓝色(0,0,255)
var start = null;

function ani(timestamp) {
    if (!start) start = timestamp;
    var progress = timestamp - start;
    var color = parseInt(Math.min(progress / 1000 * 255, 255));
    var red = "rgb("+(255-color)+",0,"+color+")";
    console.log(red);
    var blue = "rgb("+color+",0,"+(255-color)+")";
    box.style.background = "linear-gradient(to bottom right, "+red+", "+blue+")";
    if (progress <= 1000) {
        window.requestAnimationFrame(ani);
    }
}

window.requestAnimationFrame(ani);

应该就是红色管道转化为蓝色管道,蓝色管道转化为红色管道的问题吧
可惜下面的实现不支持ie,因为ie不支持filter:url滤镜
我考虑了一下,又添加了一种别的实现方式,可惜依然不支持ie
jsbin

svg{
  position:absolute;
  visibility: hidden;
}
.filter {
  filter: url('#filter')
}
<svg>
  <filter id="filter">
    <feColorMatrix id="matrix" type="matrix" values="1 0 0 0 0
    0 1 0 0 0
    0 0 1 0 0
    0 0 0 1 0" />
  </filter>
</svg>
<button id="btn">开始变换</button>
<button id="btn2">另外一种变换</button>
//第一题
const div = document.createElement('div');
Object.assign(div.style, {
  height: '300px',
  width: '150px'
});
document.body.appendChild(div);
//第二题
Object.assign(div.style, {
  backgroundImage: 'linear-gradient(to right bottom, red, blue)'
});
//第三题
div.classList.add("filter");
const matrix = document.querySelector('#matrix');
var animationDuration = 1000;
document.querySelector("#btn").addEventListener('click', transform);

function transform() {
  const startTime = Date.now();
  step();

  function step() {
    window.requestAnimationFrame(function () {
      const currentTime = Date.now();
      let i = (currentTime - startTime) / animationDuration;
      if (i > 1) {
        i = 1
      };
      matrix.setAttribute('values',
        `${1 - i} 0 ${i} 0 0
    0 1 0 0 0
    ${i} 0 ${1-i} 0 0
    0 0 0 1 0`
      );
      if (i < 1) {
        step();
      }
    });
  }
}
//另外一种实现方式
//第一题
const div2 = document.createElement('div');
Object.assign(div2.style, {
  height: '300px',
  width: '150px'
});
document.body.appendChild(div2);
//第二题
Object.assign(div2.style, {
  background: 'linear-gradient(to right bottom, red, blue)'
});
//第三题
var animationDuration = 1000;
Object.assign(div2.style, {
  'background-blend-mode': 'difference'
});
document.querySelector("#btn2").addEventListener('click', transform2);

function transform2() {
  const startTime = Date.now();
  step();

  function step() {
    window.requestAnimationFrame(function () {
      const currentTime = Date.now();
      let i = (currentTime - startTime) / animationDuration;
      if (i > 1) {
        i = 1
      };
      let c = i * 255;
      Object.assign(div2.style, {
        background: `linear-gradient(to right bottom,red,blue),rgb(${c},0,${c})`
      });
      if (i < 1) {
        step();
      }
    });
  }
}

demo

(() => {
  let oDiv = document.createElement('div')
  oDiv.style.width = '150px'
  oDiv.style.height = '300px'
  oDiv.style.background = 'linear-gradient(to right bottom, red, blue)'
  document.body.appendChild(oDiv)
  let num = 0
  function aniGradient () {
    if (num == 100) {
      window.cancelAnimationFrame(aniGradient)
      return
    }
    num++
    // 由红蓝渐变转为蓝红,那么直接在原本的红色之前接收一个蓝色(默认隐藏掉,逐渐渐变再显示出来)
    oDiv.style.background = `linear-gradient(to right bottom, blue -${100 - num}%, red ${num}%, blue ${100 + num}%)`
    window.requestAnimationFrame(aniGradient)
  }
  window.requestAnimationFrame(aniGradient)  
})()

demo

(function() {
   // 1
  const div = document.createElement('div');
  div.style.width = "150px";
  div.style.height = "300px";
  div.style.border = "1px solid #333";
  document.body.appendChild(div);
  // 2
  div.style.background = "linear-gradient(to bottom right, red, blue)";
  let last = +new Date();
  const roll = () => {
    let now = +new Date(),
     dist = now - last,
    percent = dist / 1000,
    red = 255 - 255 * percent,
    blue = 255 * percent;
    if (dist >= 1000) {
        last = now;
    }
  // 3
   div.style.background = "linear-gradient(to bottom right, rgb("+ red +",0, "+ blue +"), rgb("+ blue +",0, "+ red +")";
    window.requestAnimationFrame(roll);
   }
  roll();
})();

在线DEMO

//=> 计算渐变角度
let width = 150;
let height = 300;
let incline = Math.sqrt(Math.pow(width,2)+Math.pow(height,2));
let angle = (180 - Math.asin(height / incline) / Math.PI * 180);

//=> 创建div
let box = document.createElement('div');
box.style.width = width + 'px';
box.style.height = height + 'px';
box.style.background = 'linear-gradient('+ angle +'deg, red, blue)';
document.body.append(box);

//=> 添加渐变动画,btn是我添加的动画开始按钮
let time = 0,
   timer = null,
   step = (180/1000)*17,
   initAgent = angle,
   flag = true;
btn.addEventListener('click',function(){
  if (!flag) return; 
  flag = false;
  initAgent += step;
  box.style.background = 'linear-gradient('+ initAgent +'deg, red, blue)';
  timer = setInterval(function(){
   if (time > 1000) {
     flag = true;
     clearInterval(timer);
   }
   initAgent += step;
   time += 17; 
   box.style.background = 'linear-gradient('+ initAgent +'deg, red, blue)';
  }, 17);
});
var target = document.createElement("div");
target.setAttribute("style", "width:300px;height:150px");
document.body.appendChild(target);
target.style.background = "linear-gradient(to bottom right, red, blue)";
tzmy commented

demo

var div = document.createElement('div');
div.style.cssText='height:300px;width:150px;background:black;';
document.body.appendChild(div);
div.style.cssText+='background:linear-gradient(to right bottom, red, blue);'

第一题

 var div = document.createElement("DIV")
 div.style.cssText = "width:150px;height:300px;";
 document.body.appendChild(div)

第二题

 var div = document.createElement("DIV")
 div.style.cssText = "width:150px;height:300px;";
 div.style.background = 'linear-gradient(to bottom right, red,50%, blue)';
 document.body.appendChild(div)

第三题

var div = document.createElement("DIV")
div.style.cssText = "width:150px;height:300px;";
document.body.appendChild(div)
var value = 100;
var tag = true
setInterval(function() {
    div.style.background = 'linear-gradient(to bottom right, red,' + value + '%, blue)';
    if (value == 100) {
        tag = true
    } else if (value == 0) {
        tag = false;
    }
    if (tag) {
        value -= 10
    } else {
        value += 10
    }
}, 100)

借助CSS动画来实时取值颜色
demo

<div id="color" class="color"></div><!--颜色参考-->
.color{
   color: red;
   background-color: blue;
   animation: color 1s linear forwards;/**控制动画时长和类型**/
}
@keyframes color {
   to{
       color: blue;
       background-color: red;
   }
}
var div = document.createElement('div');
var color = document.getElementById('color');
div.style.width = '150px';
div.style.height = '300px';
document.body.appendChild(div);
div.style.background = 'linear-gradient(to right bottom, red, blue)';
var animationState = true;
color.addEventListener("animationstart",function(){
    animationState = true;
})
color.addEventListener("animationend",function(){
    animationState = false;
})
var draw = function () {
    if(animationState){
        var style = window.getComputedStyle(color);
        div.style.background = 'linear-gradient(to right bottom, '+style.color+', '+style.backgroundColor+')';
    }
    requestAnimationFrame(draw);
};
draw();

Demo

//第一题
var div = document.createElement('div');
div.style.width=150+"px";
div.style.height= 300+'px';
document.body.appendChild(div);

 //第二题
div.style.background= 'linear-gradient(to bottom right, red, blue, red)';
div.style.backgroundSize='  150% 200%';
     
//第三题    
function changeColor(){        
    div.style.backgroundSize='  220% 200%';
    div.style.backgroundPositionX="100%";
    div.style.backgroundPositionY="100%";
    div.style.transition='all  1s';       
}
var btn = document.createElement('button');
 btn.innerText="改变颜色";
document.body.appendChild(btn);
btn.addEventListener('click',changeColor);

demo

    <button id="reset">reset</button>
function go() {
    let divEle = document.createElement('div');
    divEle.style.cssText = 'width: 150px;height: 300px;'
    document.body.appendChild(divEle)
    divEle.style.background = 'linear-gradient(to bottom right, red , blue)'
    let to255 = 0
    let to0 = 255
    let reqId = requestAnimationFrame(render)
    function render() {
        if (to255 <= 255 && to0 >= 0) {
            to255 += 4.25
            to0 -= 4.25
            divEle.style.background = 'linear-gradient(to bottom right, rgb('+ to255 +',0,' + to0 + ') , rgb(' + to0 + ',0,'+ to255 +'))';
            requestAnimationFrame(render)
        } else {
            cancelAnimationFrame(reqId)
        }
    }
}
go()
document.getElementById('reset').addEventListener('click', function() {
    document.querySelector('div').remove()
    go()
})

Demo

<html>
<body>
</body>
</html>
// 第一题
var bNode = document.querySelector("body");
var divNode = document.createElement("div")
divNode.setAttribute(
  'style',
  'width:150px;' + 'height:300px;'+ "background: blue"
);
bNode.appendChild(divNode);

// 第二题
divNode.style.background = "linear-gradient(to bottom right,red, blue)";

DEMO

`

 // 使用createElement方法创建一个宽高150*300px的div元素插入到body

    function createDiv() {

        let elem = document.createElement('div');
        elem.setAttribute('style', 'width:150px;height:300px;');
        document.body.appendChild(elem);

    }

    // 设置style实现左上角到右下角的red到blue渐变
    function setGradient() {

        let oDiv = document.getElementsByTagName('div');
        oDiv[0].style = oDiv[0].getAttribute('style') + "background:linear-gradient(120deg, red 0%, blue 100%)"

    }

    // 渐变动画
    function annimateGraduebt() {

        let oDiv = document.getElementsByTagName('div');
        let n = 0;

        setInterval(function() {


            if (n === 100) {
                oDiv[0].style = oDiv[0].getAttribute('style') +
                    "background:linear-gradient(120deg,blue 0%,red 0%, blue 100%)"
                n = 0;
            } else {
                oDiv[0].style = oDiv[0].getAttribute('style') +
                    "background:linear-gradient(120deg,blue 0%,red " + (n++) + "%, blue " + (100 + n) + "%)"
            }

        }, 20)

    }

    // 1
    createDiv();

    // 2
    setGradient()

    // 3
    annimateGraduebt();

`

online demo

    //第一题
    let div = document.createElement('div')
    div.style.cssText = 'width:300px;height:150px;color:#fff;'
    div.innerText = 'hover to change'
    document.body.appendChild(div)
    //第二题
    div.style.backgroundImage = 'linear-gradient(to bottom right, red, blue)'
    //第三题
    div.addEventListener('mouseenter', () => {
        div.style.backgroundImage = 'linear-gradient(to bottom right, red, blue)'
        addLinearColor(div)
    })
    function addLinearColor(div){
        let start=Date.now()

        function step(){
            let toColorR = 0.255 * (Date.now()-start)
            let toColorB = 255 - toColorR

            let fromColorR = toColorB
            let fromColorB = toColorR
            
            if(toColorR<255){
                div.style.backgroundImage = `linear-gradient(to bottom right, rgb(${fromColorR},0,${fromColorB}), rgb(${toColorR}, 0, ${toColorB}))`
        
                window.requestAnimationFrame(step)
            }else{
                div.style.backgroundImage = `linear-gradient(to bottom right, rgb(0,0, 255), rgb(255, 0, 0))`
            }
        }
        window.requestAnimationFrame(step)
    }

codepen demo here

//习题: https://github.com/zhangxinxu/quiz/issues/30

// 1
let div = document.createElement("div");
div.style.height = "150px";
div.style.width = "300px";
document.body.appendChild(div);

// 2
div.style.background = "linear-gradient(to left top, blue, red)";
// 或者
div.style.background = "linear-gradient(165deg,red,blue)";

// 3  
//每秒重复60次,所以循环60
function clicks(){
  let progress= 0;
  window.requestAnimationFrame(linerGradientChange);
  function linerGradientChange(timestamp){
    progress += 1; 
    let color = parseInt(Math.min(progress / 60 * 255, 255));
    let blue = "rgb("+color+",0,"+(255-color)+")";
    let red = "rgb("+(255-color)+",0,"+color+")";
    div.style.background = "linear-gradient(165deg,"+red+","+blue+")";
    if (progress <= 60) {
      window.requestAnimationFrame(linerGradientChange);
    }
    
  }
}

// 兼容性参考 :https://www.zhangxinxu.com/wordpress/2013/09/css3-animation-requestanimationframe-tween-%E5%8A%A8%E7%94%BB%E7%AE%97%E6%B3%95/
  1. 通常我们使用JS给DOM元素设置style样式的时候,不通过改变style属性值,因为容器覆盖以前的样式,然后.style.xxx这样的方式不会有性能问题,即使有很多行,因为浏览器它会自动合并成一次解析。
  2. to bottom right,这样无论宽高比例是多少都没有问题。没有to就是从右下方开始。
  3. CSS渐变本质上是backgroundImage,是无法transition或者animation的,但可以使用JS。seasonley的方法就是大多数人实现的合集,非常感谢。但是非常遗憾,虽然花了很多功夫,但是对于复杂色值,其颜色变化可能并不是最舒服的那种,可能就像早期的Safari色值变化,而且如果有Alpha透明度变化,就很困难了。
  4. XboxYan的方法比较接近:我们可以借助animation或者transition本身的颜色变化来赋值,实现更简单,更准确,更接近原生的backgroundImage色值变化效果。我写的小demo:https://output.jsbin.com/hojesabawe
  5. 颜色转换的技巧。任意颜色转换为RGB(A)色值,给DOM元素赋值,然后使用getComputedStyle()获取。举个例子,请把色值skyblue转换成RGB色值,div.style.color = 'skyblue'; getComputedStyle(div).color -> RGB色值,所有浏览器都是这样的,包括IE。

sorry,忘记录播了。

感谢张老师的精彩分享 @zhangxinxu ,同时对 @XboxYan 清奇的思路表示佩服,受大神的启发,封装了个通用方法来作为第 3 题的补充:

/**
 * 颜色过渡效果构造器
 * @descr: color1 渐变过渡到 color2,color2 渐变过渡到 color1
 * @param {string} color1 - 颜色1,可以是:颜色关键字/HSL(A)/RGB(A)/HEX
 * @param {string} color2 - 颜色2,同上
 * @param {time} number - 完成过渡效果所需的时间,单位:秒
 * @param {string} [timingFn] - 过渡效果的动画函数关键词,缺省为 linear
 */
function TransColor(color1, color2, time, timingFn) {
  timingFn = typeof timingFn === 'undefined' ? 'linear' : timingFn;
  this.elm = document.createElement('i');
  this.color1 = this.getRGB(color1);
  this.color2 = this.getRGB(color2);
  this.transferring = false;
  this.done = false;

  this.elm.style.position = 'absolute';
  this.elm.style.top = '-99em';
  this.elm.style.border = '1px solid';
  this.reset();
  this.elm.style.transition = ['border-color', time + 's', timingFn].join(' ');
  document.body.appendChild(this.elm);
}

TransColor.prototype = {
  constructor: TransColor,
  getRGB: function(color) {
    var result;
    var elm = document.createElement('i');

    elm.style.position = 'absolute';
    elm.style.color = color;
    document.body.appendChild(elm);
    result = getComputedStyle(elm).color;
    document.body.removeChild(elm);

    return result;
  },
  reset: function() {
    this.elm.style.borderColor = [this.color1, this.color2].join(' ');
  },
  getFrame: function() {
    if (!this.transferring) {      
      this.transferring = true;
      this.elm.offsetHeight;  // 触发重绘
      this.elm.style.borderColor = [this.color2, this.color1].join(' ');
    }

    var computedColor = getComputedStyle(this.elm).borderColor
      .match(/rgba*\(\d+(,\s*\d*\.*\d+){2,3}\)/gi);

    this.done = computedColor[0] === this.color2;

    if (this.done) {
      this.transferring = false;
      this.reset();
    }

    return {
      color1: computedColor[0],
      color2: computedColor[1],
      done: this.done
    }
  }
};

使用:(以第 3 题为例)

var transColor = new TransColor('red', 'blue', 1);  // 初始化一个实例

var render = function() {
  // getFrame 方法返回 color1 到 color2 和 color2 到 color1 渐变的中间颜色
  var animate = transColor.getFrame();

  // 这里是要渲染的元素
  box.style.background = 'linear-gradient(to right bottom, ' +
    animate.color1 + ', ' +
    animate.color2 + ')';

  if (animate.done) {  // done 为 true 时表示过渡效果结束
    return;
  }

  requestAnimationFrame(render);
};

render();