站内搜索

本次搜索找到结果 23 条

最近我们设计师反馈,他想要做如下的一个加载动画。但是要么效果好的导出的 GIF 体积特别大,看了下有 8M 多了,要么体积小的 GIF 效果又特别不清楚。然后我看了下效果,发现其实用 SVG 动画来实现应该比较简单,于是就和设计师要了一下原始的稿子导出成 SVG 后处理了下。

将 AE 动效稿子转成 SVG 动画的话 Airbnb 有出过一款 [Lottie][1] 的工具。通过它的 AE 插件 Bodymovin 能够以 JSON 的形式导出动画信息和素材。然后在网页上使用 bodymovin.js 动画播放库载入该 JSON 素材即可完成动效的转换。具体的使用教程可以参考 Youtube 视频[《How to export an animation with Bodymovin》][2]。

使用 Bodymovin 是真的非常方便,不过介于设计师需要的效果比较简单,为了这个效果而每次去加载一个几十KB的基础库和JSON文件实在是没有必要。所以我这里就基于 SVG + CSS 动画来实现了下,最终的效果如下。最终体积也就 6KB,gzip 后会更小。 下面就来跟着我一块一步步的实现它吧!这里我不会特别详细的描述每一步的基本原理,如果大家想了解 SVG 动画的基础知识的话可以先看看我之前写的文章[《SVG 动画实践》][3]。

奇舞团有一个传统项目,每年年会由我在现场写一个抽奖程序,所有人一起review代码,以确保抽奖算法正确且公平,然后愉快滴开始抽奖。

现场写的抽奖程序不仅要公平无bug,而且还要有一定的趣味性,且不能和往年的重复。

2017年年会我写了一个随机抽纸牌中奖1的程序,而今年年会,我灵机一动????,决定写一个更有(不)趣(正)味(经)的抽奖程序。

长话短说,我们就着代码一步步往下看。

首先是常规随机洗牌三连:

function random(m, n) {

  return m + Math.floor(Math.random() * n);

}

function randomItem(arr, from = 0, to = arr.length) {

  const index = random(from, to);

  return {

    index,

    value: arr[index],

  };

}

function shuffle(arr) {

  for(let i = arr.length; i > 0; i--) {

    const {index} = randomItem(arr, 0, i);

    [arr[index], arr[i - 1]] = [arr[i - 1], arr[index]];

  }

  return arr;

}

上面的代码没有什么特别的,只是一个朴实的洗牌算法,额外封装两个随机函数,因为后面的代码中还要使用。对比一下,2017年版的代码更加“妖艳”:

function* generatePoker() {

  const points = ['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K'];

  yield* points.map(p => ['♠️', p]);

  yield* points.map(p => ['♣️', p]);

  yield* points.map(p => ['♥️', p]);

  yield* points.map(p => ['♦️', p]);

}

const cards = generatePoker();

class PickedCards {

  constructor(key, storage = localStorage) {

    this.key = key;

    this.storage = storage;

    this.cards = JSON.parse(storage.getItem(key)) || [];

    this.cardSet = new Set(this.cards.map(card => card.join('')));

  }

  add(card) {

    this.cards.push(card);

    this.cardSet.add(card.join(''));

    this.storage.setItem(this.key, JSON.stringify(this.cards));

  }

  has(card) {

    return this.cardSet.has(card.join(''));

  }

  clear() {

    this.storage.clear();

  }
}

const pickedCards = new PickedCards('pickedCards');

function* shuffle(cards, pickedCards) {

  cards = [...cards];

  cards = cards.filter(card => !pickedCards.has(card));

  let len = cards.length;

  while(len) {

    const i = Math.floor(Math.random() * len);

    pickedCards.add(cards[i]);

    yield cards[i];

    [cards[i], cards[len - 1]] = [cards[len - 1], cards[i]];

    len--;
  }
}

2017版的随机扑克牌代码

有了上面朴实的随机代码,理论上我们就可以愉快滴抽奖了:

function random(m, n) {

  return m + Math.floor(Math.random() * n);

}

function randomItem(arr, from = 0, to = arr.length) {

  const index = random(from, to);

  return {

    index,

    value: arr[index],

  };

}

function shuffle(arr) {

  for(let i = arr.length; i > 0; i--) {

    const {index} = randomItem(arr, 0, i);

    [arr[index], arr[i - 1]] = [arr[i - 1], arr[index]];

  }

  return arr;

}

let members = ['胖虎', '强夫', '静香', '大雄', '哆啦A梦', '吕布', '张飞', '关羽', '刘备', '曹操', '孙权', '周瑜',

  '黄盖', '赵云', '吕蒙', '孙悟空', '猪八戒', '唐僧', '沙悟净', '光头强', '熊大', '熊二',

  '喜洋洋', '美羊羊', '红太狼', '灰太狼',

];

console.log(shuffle(members).slice(-3)); // 抽取3名获奖者

当然,我们不会只是这么无聊滴抽取,还是要玩点花样,不然怎么好意思说自己是前端呢?

HTML必须有:

<!DOCTYPE html>

<html>

<head>

  <meta charset="utf-8">

  <meta name="viewport" content="width=device-width">

  <title>一起抽奖吧</title>

  <link rel="stylesheet" href="style.css">

</head>

<body>

  <div id="control"><button id="start">开始</button><button id="clear">清空</button></div>

  <div id="track">

    <div><span class="horse"></span><span class="player">1</span></div>

    <div><span class="horse"></span><span class="player">2</span></div>

    <div><span class="horse"></span><span class="player">3</span></div>

    <div><span class="horse"></span><span class="player">4</span></div>

    <div><span class="horse"></span><span class="player">5</span></div>

    <div><span class="horse"></span><span class="player">6</span></div>

  </div>

  <script src="app.js"></script>

</body>

</html>

CSS简单写一个:

html, body {

  padding: 0;

  margin: 0;

  width: 100%;

  height: 100%;

}

#control {

  text-align: center;

  line-height: 120px;

}

#control button {

  font-size: 2rem;

  margin: 0 10px;

}

#track {

  max-width: 1250px;

  max-height: 500px;

  border-top: solid 1px #aaa;

}

#track div {

  position: relative;

  height: 100px;

  line-height: 100px;

  font-size: 1.5rem;

  padding: 0 10px;

  color: #aaa;

}

#track .player {

  float: right;

}

#track .horse {

  display: inline-block;

  font-size: 5rem;

  transform: scale(-1, 1);

  position: absolute;

  left: 850px;

  top: 0;

  z-index: 99999;

}

#track .horse span {

  display: inline-block;

  transform: scale(-1, 1);

}

#track div:nth-child(2n) {

  background: #666;

}

#track div::after {

  content: ' ';

  position: absolute;

  left: 930px;

  width: 20px;

  height: 100%;

  background: #333;

}

这个玩法呢,就是个跑马小游戏:

接下来,我们开始完善JS代码。为了记录抽奖结果和连续抽奖(每个人只能中一次奖),避免不小心刷新了页面,导致结果丢失,我们用localStorage存一下:

const prizeStorageKey = 'prize10';

function addResults(players) {

  const result = getResults();

  result.push(...players);

  localStorage.setItem(prizeStorageKey, result.join());

}

我们如果不小心刷新了页面,重新开始抽奖之前,我们要把已经中过奖的小伙伴从列表里剔除:

function getResults() {

  const result = localStorage.getItem(prizeStorageKey);

  return result ? result.split(',') : [];

}

function filterWinner(members) {

  const winners = new Set(getResults());

  return members.filter(m => !winners.has(m));

}

members = filterWinner(members);

然后我们可以点【开始】按钮抽奖,点【清除】按钮清除localStorage记录。

const startBtn = document.getElementById('start');

const clearBtn = document.getElementById('clear');

startBtn.addEventListener('click', async () => {

  startBtn.disabled = 'disabled';

  clearBtn.disabled = 'disabled';

  // 重新洗牌

  shuffle(members);

  // 取出最后6名同学,倒数3名中奖,剩下3名凑数

  const candidates = members.slice(-6).reverse();

  // 将中奖结果保存到localStorage中

  addResults(candidates.slice(0, 3));

  members.length -= 3;

  // 开始跑马程序

  await race(candidates);

  startBtn.disabled = '';

  clearBtn.disabled = '';

});

clearBtn.addEventListener('click', () => {

  // 清除所有中奖记录

  localStorage.removeItem(prizeStorageKey);

});

接下来就是实现关键的跑马程序了。

其实我们的中奖结果在跑马程序开始前就已经出来了,跑马程序只是运行动画效果,和中奖结果无关。

最简单的一种方式就是根据排名依次从短到长,生成跑马总时间,然后将6个人随机到不同的赛道开始跑马:

function race(candidates) {

  const durations = [];

  for(let i = 0, duration = 0.9; i < candidates.length; i++) {

    durations.push(duration);

    // 每一名次随机增加 0.02 ~ 0.05 的时间

    duration += random(2, 5) * 0.01;

  }

  const players = shuffle([...candidates.entries()]);
  ...
}

但是这样有个问题,就是跑马的时候,名次落后的时间长速度慢,名次靠前的速度快始终跑在前面,胜负毫无悬念,也就失去了赛马的意义。所以要做随机,以保留悬念。

产生随机有很多种方法,这里用一种最简单的方法,就是把一次比赛分成若干个小阶段,每个小阶段分配一个基准时间,但是允许每个选手在该阶段时间有一定的正负扰动。比如:

A选手跑完全程时间8秒钟,B选手跑完全程时间为10秒钟,我们第一阶段先取路程的1/4,A跑完1/4程的基准时间是2秒,B是2.5秒,假设扰动参数为正负0.5,那么A跑完1/4程的时间最多是2+0.5=2.5秒,而B跑完1/4程的时间最少是2.5-0.5=2.0秒,这样就有可能在前1/4赛程里A选手反而落后B选手了。多分几个赛程,就可以有足够的悬念。

我们先定义划分赛程的函数:

function partRace(durations, factor) {

  // 根据赛程总时间 duration 和 factor 来划分赛程

  // 赛程所用基准时间为 duration * factor,扰动 -0.1 ~ +0.1

  const subDuration = durations.map(d => d * factor * random(9, 11) / 10);

  subDuration.map((d, i) => {

    durations[i] -= d;

    return durations[i];

  });

  return subDuration;

}

这样我们把全程划分4段赛程:

function race(candidates) {

  const durations = [];

  for(let i = 0, duration = 0.9; i < candidates.length; i++) {

    durations.push(duration);

    // 每一名次随机增加 0.02 ~ 0.05 的时间

    duration += random(2, 5) * 0.01;

  }

  // 划分4段赛程

  const round1 = partRace(durations, 0.25);

  const round2 = partRace(durations, 0.33);

  const round3 = partRace(durations, 0.5);

  const round4 = durations.map(d => d + 0.1);
  ...
}

这里面还有一个小技巧,我们划分赛程的时候,给最后一轮留下10%的时间,这是为了避免前面几轮赛程积累的随机扰动使得最后一程的时间太短。

这样我们就可以绘制赛马动画了:

function partRace(durations, factor) {

  // 根据赛程总时间 duration 和 factor 来划分赛程

  // 赛程所用基准时间为 duration * factor,扰动 -0.1 ~ +0.1

  const subDuration = durations.map(d => d * factor * random(9, 11) / 10);

  subDuration.map((d, i) => {

    durations[i] -= d;

    return durations[i];

  });

  return subDuration;

}

function race(candidates) {

  const durations = [];

  for(let i = 0, duration = 0.9; i < candidates.length; i++) {

    durations.push(duration);

    // 每一名次随机增加 0.02 ~ 0.05 的时间

    duration += random(2, 5) * 0.01;

  }

  const players = shuffle([...candidates.entries()]);

  trackEl.innerHTML = players.map((p, i) => {

    return `<div>

 <span class="horse">${randomItem(['????', '????', '????', '????']).value}</span>

 <span class="player">${p[1]} ${i + 1}</span>

 </div>`;

  }).join('');

  // 划分4段赛程

  const round1 = partRace(durations, 0.25);

  const round2 = partRace(durations, 0.33);

  const round3 = partRace(durations, 0.5);

  const round4 = durations.map(d => d + 0.1);

  const results = ['????', '????', '????', '????', '????', '????'];

  const T = 8000;

  const horses = document.querySelectorAll('.horse');

  const promises = [];

  for(let i = 0; i < horses.length; i++) {

    const horse = horses[i];

    const idx = players[i][0];

    promises.push(raceHorse(horse, round1[idx] * T)

      .then(() => {

        return raceHorse(horse, round2[idx] * T, 30 + trackLen / 4);

      })

      .then(() => {

        return raceHorse(horse, round3[idx] * T, 30 + 2 * trackLen / 4);

      })

      .then(() => {

        return raceHorse(horse, round4[idx] * T, 30 + 3 * trackLen / 4);

      })

      .then(() => {

        horse.innerHTML = `<span>${results[idx]}</span>${horse.innerHTML}`;

        return raceHorse(horse, 0.1 * T, 30 + trackLen, 100);

      }));

  }

  return Promise.all(promises);
}

具体的raceHorse就是一个简单的DOM匀速动画绘制过程:

function raceHorse(horseEl, duration, from = 30, by = trackLen / 4) {

  return new Promise((resolve) => {

    const startTime = Date.now();

    requestAnimationFrame(function f() {

      let p = (Date.now() - startTime) / duration;

      p = Math.min(p, 1.0);

      horseEl.style.left = `${from + p * by}px`;

      if(p < 1.0) requestAnimationFrame(f);

      else resolve();

    });
  });
}

把完整的代码汇总一下:

function random(m, n) {

  return m + Math.floor(Math.random() * n);

}

function randomItem(arr, from = 0, to = arr.length) {

  const index = random(from, to);

  return {

    index,

    value: arr[index],

  };

}

function shuffle(arr) {

  for(let i = arr.length; i > 0; i--) {

    const {index} = randomItem(arr, 0, i);

    [arr[index], arr[i - 1]] = [arr[i - 1], arr[index]];

  }

  return arr;

}

const prizeStorageKey = 'prize10';

function getResults() {

  const result = localStorage.getItem(prizeStorageKey);

  return result ? result.split(',') : [];
}

function addResults(players) {

  const result = getResults();

  result.push(...players);

  localStorage.setItem(prizeStorageKey, result.join());

}

function filterWinner(members) {

  const winners = new Set(getResults());

  return members.filter(m => !winners.has(m));

}

let members = ['胖虎', '强夫', '静香', '大雄', '哆啦A梦', '吕布', '张飞', '关羽', '刘备', '曹操', '孙权', '周瑜',

  '黄盖', '赵云', '吕蒙', '孙悟空', '猪八戒', '唐僧', '沙悟净', '光头强', '熊大', '熊二',

  '喜洋洋', '美羊羊', '红太狼', '灰太狼',

];

members = filterWinner(members);

const startBtn = document.getElementById('start');

const clearBtn = document.getElementById('clear');

startBtn.addEventListener('click', async () => {

  startBtn.disabled = 'disabled';

  clearBtn.disabled = 'disabled';

  // 重新洗牌

  shuffle(members);

  // 取出最后6名同学,倒数3名中奖,剩下3名凑数

  const candidates = members.slice(-6).reverse();

  // 将中奖结果保存到localStorage中

  addResults(candidates.slice(0, 3));

  members.length -= 3;

  // 开始跑马程序

  await race(candidates);

  startBtn.disabled = '';

  clearBtn.disabled = '';

});

clearBtn.addEventListener('click', () => {

  // 清除所有中奖记录

  localStorage.removeItem(prizeStorageKey);

});

const trackLen = 820; // 205 * 4

const trackEl = document.getElementById('track');

function partRace(durations, factor) {

  // 根据赛程总时间 duration 和 factor 来划分赛程

  // 赛程所用基准时间为 duration * factor,扰动 -0.1 ~ +0.1

  const subDuration = durations.map(d => d * factor * random(9, 11) / 10);

  subDuration.map((d, i) => {

    durations[i] -= d;

    return durations[i];

  });

  return subDuration;
}

function race(candidates) {

  const durations = [];

  for(let i = 0, duration = 0.9; i < candidates.length; i++) {

    durations.push(duration);

    // 每一名次随机增加 0.02 ~ 0.05 的时间

    duration += random(2, 5) * 0.01;

  }

  const players = shuffle([...candidates.entries()]);

  trackEl.innerHTML = players.map((p, i) => {

    return `<div>

 <span class="horse">${randomItem(['????', '????', '????', '????']).value}</span>

 <span class="player">${p[1]} ${i + 1}</span>

 </div>`;

  }).join('');

  // 划分4段赛程

  const round1 = partRace(durations, 0.25);

  const round2 = partRace(durations, 0.33);

  const round3 = partRace(durations, 0.5);

  const round4 = durations.map(d => d + 0.1);

  const results = ['????', '????', '????', '????', '????', '????'];

  const T = 8000;

  const horses = document.querySelectorAll('.horse');

  const promises = [];

  for(let i = 0; i < horses.length; i++) {

    const horse = horses[i];

    const idx = players[i][0];

    promises.push(raceHorse(horse, round1[idx] * T)

      .then(() => {

        return raceHorse(horse, round2[idx] * T, 30 + trackLen / 4);

      })

      .then(() => {

        return raceHorse(horse, round3[idx] * T, 30 + 2 * trackLen / 4);

      })

      .then(() => {

        return raceHorse(horse, round4[idx] * T, 30 + 3 * trackLen / 4);

      })

      .then(() => {

        horse.innerHTML = `<span>${results[idx]}</span>${horse.innerHTML}`;

        return raceHorse(horse, 0.1 * T, 30 + trackLen, 100);

      }));

  }

  return Promise.all(promises);

}

function raceHorse(horseEl, duration, from = 30, by = trackLen / 4) {

  return new Promise((resolve) => {

    const startTime = Date.now();

    requestAnimationFrame(function f() {

      let p = (Date.now() - startTime) / duration;

      p = Math.min(p, 1.0);

      horseEl.style.left = `${from + p * by}px`;

      if(p < 1.0) requestAnimationFrame(f);

      else resolve();

    });

  });

}

最终的效果:

以上就是今年奇舞团年会的抽奖程序,大家有什么想法可以关注我们的GitHub仓库2与我们交流。

2019年年会的抽奖程序我也已经想好了,只会更加有趣,不过暂时不能透露????,2019年年会继续加油~

文内链接: https://github.com/75team/raffle/tree/master/2017 https://github.com/75team/raffle

学姐,女朋友嫌我太忙要跟我分手怎么办?

编者按:我是年已过四分之一百赖着不走的拉高奇舞团整体颜值的星计划的小仙女钉子户。

有一天晚自习,星计划某宝宝:学姐,女朋友嫌我太忙要跟我分手怎么办? 我:??? 某宝宝:你男朋友太忙你会跟他分手吗? 我:首先。。。分分分,必须分。

第五届360前端星计划在周五的小组项目汇报和月影大大的走心总结中顺利结束~

五年,五届星计划,为什么我们要做前端星计划,我觉得最好的回答还是月影大大说的“前端星能够影响更多的学生,不管他们是否最终选择360,总是能让这个行业变得更好,而一个行业变好了,那么作为行业中的每个从业人员,最终都能获益。”

关于课程

今年的星计划课程,我们总结往年的经验,采纳前一届学生的建议,仔细斟酌适合星计划学生学习的难易度,邀请了我司最具前端开发经验的前辈们和有授课经验的讲师们一起开课程确认会讨论出星计划学生需要掌握的知识,更系统的安排了此次星计划的课程。

day1

文博HTML,安佳的CSS和月影JavaScript(1)

day2

月影的JavaScript(2),李老师的W3C标准,二哥的命令行和仁义的正则表达式。

day3

李喆明的NodeJS(真香),月影(对,这个人又出现了,我没有写错)的HTTP协议与服务端编程和第一届星计划学员田东东的前端工程化浅析。

day4

智杰的设计模式,赵岩的优化首屏渲染和第二届星计划学员刘冰晶的动画。

day5

刘宇晨的浅谈web安全,王峰老师的 vue 开发实践与实用的前端工具函数,李老师的技术翻译入门和磊哥的代码质量和单元测试。

day6

项目评比 在课程的第一天,月影给大家布置了小组作业,我们同学分了六个组来做自己组的项目。大家也都尽自己最大努力做了自己组想做的东西并且得到了讲师的肯定。

第一名:斗图王

第二名:在线五子棋对战

第三名:实时共享画板

评委大大们

小故事讲起来

在课间休息吃饭及晚自习时间听到很多人的小故事,每组一则小故事吧~~

别的组怎么都这强队

某天中午扎着帅气小辫的小伙子ZYK跟我们讲他高中时候因为打球跟人打架,我开玩笑说他有暴力倾向要把他赶出教室,最后劝他现在长大了不能太急躁。他还教了我一个新词:洗衣机男。某天晚自习,星计划过程中被我们diss担当的符宝宝突然问我:学姐,女朋友嫌我太忙要跟我分手怎么办?我:???你这么小就有女朋友了吗?符宝宝:我们组除了xxx和xxx没有对象,其他人都有。我:纳尼,那一定要分手,果断分,你忙啥呀忙?符宝宝:星计划作业多项目也写不完。我:那快让我看看你们女朋友的照片。看完之后:哎呀我健哥女朋友太可爱了太萌了好喜欢健哥了不起了啊,然而符宝宝只给我看了他女朋友的耳朵(微笑冷漠要跟你绝交脸顺便哼一下.gif)。

你,给我通宵改!队

我永远也忘不了我畅哥(一个小女生)在晚上十一点多,拍着桌子对杜宝宝说:你,给我通宵改!!!后来知道畅哥为了让杜宝宝好好做项目收了杜宝宝的手机,直到他们晚上准备回才给他(捂脸并向杜宝宝投去同情的目光.jpg)。然后,剩下的几天我都怕吓着杜宝宝,中午吃完饭他午睡我就跟他说我不是畅哥,你不要怕我,赶紧休息一会,通宵改代码也挺累的。本来我以为他们这么不客气是以前认识,后来才知道他们也是来这里才认识。后来结束的时候我给杜宝宝包里装了满满的吃的让他带回去吃。

菜鸡抱团取暖队

这个组有两个“组花”,一个是我说的一个是自封的(我并不想承认,但是后来被他的诚意打动),组员都很好玩,五个男生带一个女生,所以唯一一个女生也是组花,但是杨健民(这块一定要实名)这个人非要说我们崔宝宝不是,他才是,在我说崔宝宝是组花的时候疯狂向我明示暗示他才是组花(呵呵,你美,你最美行了吧)。还有,说要给我报屈屈发我丑表情包的仇给屈屈月影做丑丑的表情包的周宝宝和迪迪,你们明明是在对我造成二次伤害啊,为什么他们怎么着都好看你这个算法有问题好吧我承认他们长的精致。这个组的项目“斗图王”也是我们本次星计划项目评比的第一名。

灵魂画手队

我心中的学霸组啊,没有多余的话,就是认真学习,认真做项目。还记得第一天晚自习,我问独自一人在他们组位置上坐着的江宝宝:为什么你们组就剩下你一个人还在啊,其他人呢?江宝宝:他们离得远,都回去了。后来知道他是他们组组长,因为学姐学长们让着他所以他是组长哈哈哈哈哈。还有突然被这个组的宝宝问到我为什么每天都这么开心呀~

百岁山队

这个队有月影的亲戚吴x亮啊,而顶着月影亲戚光环的吴宝宝也没让人失望,被我们认为是本届星计划最帅的宝宝。月影看到这个队名的时候说:百岁山给了你们多少钱,我娃哈哈双倍哈哈哈哈哈。

怎么改bug都不队

他们非要给自己组名叫“铁血真汉子”,因为他们组,没有一个女生!!!分组的时候没抢到(搞笑嘞,你以为女组员跟女朋友一样,组织给你分配呀),后来为了显得不是很惨修改了组名我还挺满意。当然,我对这个组也很满意,每个宝宝都有自己的特点。

end

天天黑你们但更多的时候给你们喊加油,太多次假装敷衍的夸你们代码写的真好,页面写的真好看,其实真的想夸奖你们,很棒~希望你们能永远记得自己最初追求的是什么,跟自己和解,不要给自己压力,好好学习,天天开心。至于面试,这是一个发现自己有哪些知识还没学到或者还没有掌握的很好的过程,没过的话之后也要总结经验好好学习呀。我们不都既是会哭的宝宝,也是坚强勇敢的大人嘛。

食堂饭好吃希望你们以后会来360!

虽然只有短短的六天,但是我会记得所有你们带给我的开心。哈哈哈哈哈,突然想到怕你们吃不饱买太多吃的最后没吃完让你们给包里给口袋装带走在火车上吃,一个技术培训搞成这样的结尾想到画面也是相当的喜感哈哈哈哈哈。

最后的最后,感谢~

我要以一个前端星计划主负责部门的主负责人实名感谢~ 以下顺序是我突然想起谁说谁,可能不全 //主要想说明随意你们都是我喜欢的人呀,并列的那种~

设计师凯军

感谢设计师大哥凯军不管在什么时候都能帮我设计出我想要的东西,基本上我找他做的东西都是突然出来的任务,他可能在做别的东西,忽然被一路小跑过去找他的我打断,然后开始说我的需求,我要什么时候要,最后凯军总是在吃饭时间给我发让我看满意不,然后我觉得不是我喜欢的样子会跟他说吃完饭下午跟他说,下午去找他就说这个不够青春活力有朝气,我要那种很阳光的感觉,就是一看就很青春的(哈哈哈哈哈哈哈,这里的对话有个段子,算了,先忍着,我先自己开心着^_^),希望我能早点唤醒凯军的少女心(如果有的话哈哈哈哈哈)

月影

感谢月影大大赞助(自己并不知道自己还赞助了)的 SpriteJS 好看的文化衫和凯军帮设计的超Q的本子还有好多好多(连我这个偷偷“帮”他赞助的都不知道有哪些东西)。尤其感谢月影大大给我们分享的他的工作经验以及在之后的工作中会遇到的各种各样的而他已经经历过并且解决了的问题。怎么说呢,没有月影的话星计划不是齐齐整整的吧,是月影表现出来的对星计划的在乎感染的我在整个过程中愿意对星计划负责任也愿意去为学生着想愿意去为他们做一些事情。PS:感谢月影对我的夸奖(小脸一红且害羞.gif)。

李喆明

感谢李喆明一直陪着我。因为上一届星计划我发现学生在做项目或者做作业时会遇到一些自己解决不了的问题,而有时候我也没有能为他们解决问题,所以今年我在星计划开始前跟李喆明说我们星计划晚上需要有人跟着自习,他们遇到解决不了的问题需要比我技术厉害很多的人帮他们解决,没想到李喆明超级上心,快到点了会问我他是不是要下楼陪学生了,然后他就带着自己电脑下楼没学生问问题就自己干活。因为我们陪他们到很晚,每天晚上吃完饭也会交流面试啊工作啊生活啊什么的,直到后来,我俩生生活成了这群星计划宝宝的知心哥哥和知心姐姐,不,我可能像妈妈一点吧,担心他们下午会饿给他们准备下午茶,担心他们会冷或者会热一天看好多遍教室的温度(顺便掌握了调中央空调的技能),面试没面好安慰他们鼓励他们学习。除了我,最了解他们情况的就是李喆明了,每天晚上,李喆明过去我都得给他讲一会今天谁谁谁又咋了谁谁谁今天又干了什么搞笑的事,谁谁谁今天上课回答问题回答的特别好等等等等。。。有时候特别晚都十一点多了,我跟李喆明说你回去吧,两个人是陪一个人也是还不如一个人,至少有一个人都能早点下班,然后他准备走的时候说那我真走了啊,然后我一说你就这样抛下我了他又把包放下跟我一起陪学生哈哈哈哈哈。

李冬杰

感谢李冬杰我需要帮忙的时候几乎随叫随到除了早晨比较早的时候。早晨帮我接学生,中午我休息的时候帮我做一些需要我做的事情。还有,所有,需要苦力的时候我都喊他了(希望李冬杰不要知道我只喊他了哈哈哈哈哈哈),他也任(chao)劳(ji)任(kai)怨(xin)跑来跑去。btw,李冬杰,我单方面宣布一周不欺负你!but,你说你是我大爷这件事我不能认,坚决不认!!!

刘冰晶

感谢刘冰晶早上跟我换时间接学生,晚上晚自习会去辅导学生做项目,后来我觉得两个人也是陪三个人也是陪(这句话重复了哈哈哈哈哈)让她先回去,至少有个人能休息好。 感谢威扬,这个是超级感谢,毕竟金主爸爸,慷慨且毫不犹豫的赞助了我们项目评比的奖品和下午茶及更多。 感谢秀婷,很开心的一起合作,她负责招聘我负责培训,两个人睡没时间的时候能够快速介入并帮助对方处理好事情。

先帅

感谢先帅,当有学生身体不舒服我没法走开帮他拿药的时候先帅帮我去医务室拿药并送到教室,比心心~

文博

感谢我的领导文博(这是一则展现求生欲的感谢[满脸微笑])在星计划过程中对我的鼓励和理解,您是最棒的,最慈祥的。答应我,你家娃以后不管男孩女孩不管叫啥都允许我现在以“赵大强”来称呼ta好吗?

小胖

感谢小胖,我需要帮助的时候随时在线,你在学校好好学习呀,快点毕业来上班,我请你吃火锅烤肉呀~

好多人

感谢李老师烁君屈屈博文等赞助的书和对学生的鼓励。感谢奇舞团、搜索、导航、企业安全等部门的qi的大佬们对星计划的支持(比个超大的心.mp4)。感谢孟倩张欢方旭美伶在这几天帮我做了很多工作。

成银

感谢成银(组)出了个李喆明刘冰晶和李冬杰帮助我,哈哈哈哈哈哈,这个感谢有点敷衍哈哈哈哈哈。谢谢成银给星计划提的意见。

星计划宝宝

感谢星计划宝宝,我永远会记得你们真挚的眼神。配合我的工作,包括我们突然折腾换会议室也不抱怨积极的搬东西,最后课程结束帮我把所有东西都收拾好送回七楼然后才离开,爱你们希望你们度过了难忘开心有收获的六天

特别开心我坚持了一周吧,虽然攒的综艺都够看一天多了,朋友圈也是好几天才刷,也经历了好几天的睡眠不足,但是真的开心,看到宝宝们脸上洋溢着收获的表情我(像老母亲一般慈祥的小仙女)表示很欣慰。

嗯,希望我们能让360前端乃至前端变得更好~

悄咪咪的广告:希望大家支持创造营的赵让和周震南,让他们出道(嘻嘻嘻,我的领导们一定看不到这句,反正月影已经答应我假装没看到了)~

可爱的宝宝有话说

哼,夸我美的我一定要留着

叶文俊宝宝:刚收到电话通知邀请我参加前端星线下学习的时候,我没有犹豫,马上就答应下来了,因为虽然杭州距离北京比较远,坐高铁也要5个多小时,还有租房子和上课请假之类的问题,但确实机会比较难得,还能交到很多有相同目标的伙伴,事实也正好符合预期,交到很多朋友,学到了很多东西,也接触了很多以前没了解过的技术,老师上课讲的也是实际工作中要用到的,所以很实用,除此之外,最让我感动的就是奇舞学院的用心,很为学生着想,因为晚上吃饭的时间比较晚,担心我们饿肚子,就为我们准备了零食,并且奇舞团每位老师都非常和蔼可亲,还有可爱的小姐姐,可爱的小哥哥,每天都在陪着我们。六天时间转瞬即逝,确实有些不舍,有缘再见。

袁蕾:在接到360前端星计划的通知时,我曾经犹豫于要不要去一个陌生的城市参加接近一周的培训。但是值得庆幸的是我最后选择了参加。如果说曾经的我学习前端时,是在一片迷茫的大雾中探索,那么360前端星计划则是为我点燃了前行的灯火。在这六天的培训里,我不仅学到了很多前端领域的知识,明确了以后的学习方向,也认识了一群很优秀的人们,学习到了很多人生的道理。真的很感谢360奇舞团举办的前端星计划,也希望大家以后都能在前端的路上越走越远,实现自己的理想。

吴晗:参加360前端星计划让我认识了很多学前端的小伙伴,他们都特别优秀,还有幸见到了很多奇舞团以及搜索的大佬老师,可爱美丽的奇舞团小姐姐们,他们的课都非常棒,让我对前端体系有了更深刻的认识,在这期间,让我意识到了自己学习方法的不足以及思考问题不全面,然后对自己未来的职业发展进行了深刻的思考,月影大大布置的大作业让我对产品项目的流程有了深刻的认识,让我对团队分工协作有了更深的体会,让我明白个人能力要和团队紧密结合在一起。最重要的是,360食堂特别好吃。

是否总觉得自学 JS、HTML、CSS小有成就但心里空虚?眼看着《HTML 从入门到精通》手下却一行代码十个 error ?莫慌!你离大牛只差一步之遥—— 360 前端星计划!!

前端星计划

360 前端星计划是由 360 前端团队主办,面向在校大学生,为培养最优秀最潜力的前端人才举办的前端技术系列培训&人才选拔项目。

关于课程

可能很多人会有疑问,我们这个培训六年了,课程是不是都一样呀。不是哦,我们的课程每年都会有调整,会结合平时工作,衡量哪些是现在的前端从业人员需要掌握的知识,然后再安排课程。今年的课程如下

明星讲师

前端星计划是奇舞团多年倾心打造的明星课程, 邀请来自奇虎360各大前端团队的技术主管以及业务线一线优秀工程师作为讲师为大家分享。

月影

360技术中台-前端开发部(奇舞团) 团长负责奇舞团的整体工作,前端届“网红”。《JavaScript王者归来》及 canvas动画库Spritejs作者。喜欢探索各种语言,深入编程本质, 执着于前沿技术并热爱分享。

贺师俊(hax)

360 高级前端架构师 十多年来一直活跃在前端和 JavaScript 社区。对多项 Web 标准有微小贡献,对 Groovy 语言并间接对 Swift 语言有微小贡献,近年来参与了诸多 ECMAScript 新草案的讨论。

赵文博

360技术中台-前端开发部 资深前端工程师360奇舞团体验技术部负责人,教育部认证HTML5讲师,拥有多年的前端开发实践经验。

李松峰

Web前端开发资深专家 资深技术图书译者,翻译出版过40余部技术及交互设计专著,现任360前端技术委员会委员、W3C AC代表。

小组项目

最后一天是项目评比啦!在课程的第一天,月影给大家布置了小组作业,我们同学组了九个队来做自己组的项目,每个组都分配了一个导师。

淡黄的长裙,蓬松的头发

项目名:flippy-bird 介绍:操纵你的小鸟, 灵巧的躲避障碍物,在原有代码基础上, 增加了模式选择, 反重力等功能。

你来猜呀队

项目名:你来猜呀 介绍:猜数字——小游戏。单人模式:自娱自乐,笑傲天梯。多人模式:轻松破冰,熟悉彼此。

new Offer()

项目名:众志成城战疫情 项目背景:2020年疫情,让我们见证了很多,很多人,很多事,都值得被铭记,值得被歌颂,值得被流传。功能特点:在提供有效信息和分享的基础上,结合专业知识,进行特效的呈现,让每一个人,每一件事都生动的呈现在用户眼前。并且随着时间推移,信息和相关数据,也会不断更新和追加。

promise.all

项目名:“拼”出offer【第二名】介绍:为前端小白工程师私人定制的小游戏,体验学习前端步步进阶的乐趣,最后以拼出offer为胜利。

offer收割机

项目名:项目直播早知道 介绍:集淘宝直播、B站直播与高校直播多个平台于一体,直播类别涉及购物、娱乐和学习,满足不同人群的需求。用户可以针对不同平台来查看和检索直播信息,并提供关注直播和直播提醒功能。

placeholder

项目名:疫情地图 介绍:一个基于echarts和node.js的疫情地图数据可视化项目。 月亮不睡我不睡 项目:疫情实时大数据报告【第三名】 介绍:使用Vue搭建网页结构,后端使用nodejs,利用puppeteer模块爬取疫情数据,前端使用echarts进行数据可视化,展示了疫情地图,疫情曲线,疫情数据表格,以及实时疫情新闻热点。

白白胖胖,充满希望

项目:和弦助手【第一名】 介绍:和弦助手小程序是基于微信小程序平台实现的一款能够查询和弦的工具。项目目标是在用户使用过程当中,能够通过选择和弦的种类、组号和调号,查询对应的和弦。并以音符、图片、音频三种方式展示。

bug maker

项目:极简匿名聊天室 介绍:这是一个简易的匿名聊天室,实现了登录注册和匿名聊天的功能,还可以查看历史记录,一起来快乐聊天吧。 由于项目展示环节太过精彩好几个项目忘记拍照 ╮(๑•́ ₃•̀๑)╭

小组被导师催更的日常

参加星计划后(ง •̀_•́)ง (*•̀ㅂ•́)و每天叫醒你的不只梦想,还有奇舞团催你上课学习做项目交笔记的老师

据说,上课+笔记会离成为大佬更近哦 ꒰๑´•.̫ • ๑꒱

来都来了,就要对你负责到底 (..•˘_˘•..)

你体验过群面前端大佬的感受吗? 你有群面前端牛人的机会吗? 不要怀疑,星计划的同学经历过,如果你想有这样的机会或体验,不要犹豫,来360前端星计划走一走。请期待奇舞周刊之后分享的前端星计划班会群面面经和小伙伴们的优秀笔记。 你不来,我不来,360星计划如何开;你以为360星计划是男女比例1 : 2吗你以为你来360星计划可以脱单吗?我告诉你吧,这些都是真的!这是程序员的浪漫,我们的代码告诉你我们的心情。 六年,六届星计划。我们希望前端星能够影响更多的学生,不管他们是否最终选择360,总是能让这个行业变得更好,而一个行业变好了,那么作为行业中的每个从业人员,最终都能获益。 受疫情影响,今年的培训只能在线上进行,这也是我们第一次在线上进行,参加星计划的学生在线上课,所有课程均已录制好在奇舞学院学习。奇舞学院(https://study.qiyun.360.cn) 是奇舞团倾心打造的知识分享平台。

为了能帮助到更多想提升自己前端能力的开发人员,星计划课程已经对外开放,想学习星计划课程的同学欢迎你来奇舞学院学习,有很多免费的课程在等着你。星计划课程只对星计划学员免费,其他同学现在有活动价(2折哦,而且还有章节可免费试听) 淡黄的长裙没有了 你还会牵着我的手(嗯嗯?)去买新出的教程吗 不然无人的街道 空荡的页面 只剩我一个人在那改着曾经的 bug

对奇舞学院有任何问题请联系: 邮箱:qiwuxueyuan@360.cn 微信:liangxz521

前端星计划课程地址: https://study.qiyun.360.cn

logo

说到视频字幕格式,一般大家都会想到 .srt, .ass 之类大家比较常用的格式。而现在说到 Web 字幕格式,大家第一反应肯定都是 [WebVTT][1]。我们知道在<video>或者<audio> 标签中要加载字幕的话,需要使用 <track> 标签将字幕文件嵌入进来。而在 [track][2] 的文档中我们会发现其实还有一种 Web 字幕格式,那就是本文的主角 TTML。

The tracks are formatted in [WebVTT format][3] (.vtt files) — Web Video Text Tracks or [Timed Text Markup Language][4] (TTML).
via: [<track>: The Embed Text Track element][5]

前段时间我开源了一个叫 [Animaris][1] 的项目,这个项目是使用 ThinkJS + MongoDB + React + Antd 开发的移动端 WebView 接口文档系统。平常大家见到的接口文档无非就是 HTTP API 接口文档,要么就是框架/库提供的接口方法文档。对于这种 WebView 的接口提供文档基本上没有。借着业务的需求我做了这么一个项目,用来解决以下两个问题:

  1. 移动端接口编写并生成可视化文档
  2. 移动端接口前端开发环境模拟问题

如果有相关需求的同学也欢迎使用,感兴趣的同学也欢迎 [star][2]。咳咳,回到本文的重点,项目开发的差不多的时候我就准备给它设计一款 LOGO 了,以下是成品效果图。虽然我没有学过绘画没有很好的设计功底,但是我相信简单的形状也能发挥不错的效果,那么我就给大家讲讲我是如何设计这款 LOGO 的。

animaris

原文:https://imnerd.org/svg-animation-in-action.html

SVG 是基于 XML 的矢量图形描述语言,可以近似理解成 HTML,所以能和 JS 以及 CSS 进行交互。 特别是 CSS,配合 CSS Animation 就能实现一些令人心旷神怡的动画,这实在是让人兴奋。下面我们就来看看 SVG 配合 CSS 实现动画的一些方法。

本文内容主要是我之前分享的文字版,若想看重点的话可以看之前的 Slide:

也可以查看分享视频:http://cloud.live.360vcloud.net/theater/play?roomid=2079

原文:https://www.h5jun.com/post/sixty-lines-of-code-animation.html

最近在给学生上课,上周六的第一堂课是关于 JavaScript 动画的内容,其中包括一些简单的动画,比如匀速或者匀加/减速的运动,也包括复杂一些的组合动画。而动画的基本原理,在我之前的文章已经有了详细的介绍。在这里,我想谈一谈的是,我们可以如何针对现代浏览器设计更加简单的 API,来实现动画的序列播放。

原文:https://www.h5jun.com/post/the-12-best-weekly.html

值得订阅的 12 份优质前端期刊

作为前端从业人员,能够及时接收行业最新最前沿技术资讯,是个人成长的一项必备技能。大家都知道前端发展迅速,每周都有新东西出现。那么行业里面究竟有哪些值得关注和订阅的前端资讯类期刊呢?小编将多年整理和收集的 12 份优质期刊分享给大家(排名不分先后)

原文:https://www.h5jun.com/post/raspberry-pi.html

Raspberry Pi(树莓派),是英国的树莓派基金会所开发的单板计算机,它是由 Eben Upton 以及他的同事设计的,最初的目的是以低价硬件及自由软件促进学校的基础计算机科学教育。

树莓派 Model B+

树莓派 Model B+

在物联网(IoT)高速发展的今天,树莓派以低廉的价格、开放的硬件/软件生态、灵活的通用输入输出(GPIO)接口,成为学习、研究物联网和万维物联网(WoT)的理想设备。