css 如何“畫”一個抽獎轉(zhuǎn)盤

主要描述的是如何運用 css 繪制一個抽獎轉(zhuǎn)盤帅戒,并運用原生 js 實現(xiàn)轉(zhuǎn)盤抽獎效果。

先來張效果圖:

布局

一般來說昧港,轉(zhuǎn)盤一般有四個部分組成:外層閃爍的燈擎椰、內(nèi)層旋轉(zhuǎn)的圓盤、圓盤上的中獎結(jié)果创肥、指針达舒。

所以html的結(jié)構(gòu)如下:

<div class="turntable-wrap">
    <div class="light" id="turntable_light"></div>
    <div class="turntable" id="turntable">
        <ul class="bg" id="turntable_bg"></ul>
        <ul class="gift" id="turntable_gift"></ul>
    </div>
    <div class="pointer disabled" id="turntable_pointer">點擊抽獎</div>
</div>

其中燈需要一直閃爍值朋,而抽獎的時候轉(zhuǎn)盤需要轉(zhuǎn)動連帶其中的中獎結(jié)果,而轉(zhuǎn)盤指針不需要轉(zhuǎn)動是固定的休弃。因此 light 吞歼、 turntable 、 pointer放在同一級別塔猾。

外層容器利用border-radius:50%實現(xiàn)從正方形變圓形的效果篙骡。border: 7px solid #b2a98d加個邊框,box-shadow: 0 0 20px #b2a98d
加點陰影丈甸,美化一下糯俗。

.turntable-wrap {
    position: relative;
    overflow: hidden;
    margin: 50px;
    width: 340px;
    height: 340px;
    border: 7px solid #b2a98d;
    border-radius: 50%;  
    box-shadow: 0 0 20px #b2a98d;
}

1. 閃爍的燈

  • 利用 js 動態(tài)的給 light 生成多個小圓點,lightNum = 18睦擂,感覺這個數(shù)字得湘,轉(zhuǎn)盤每小塊顯示 3 個燈,感覺效果最好
// 初始化燈
let lightFragment = document.createDocumentFragment();
for (let i = 0; i < this.lightNum; i++) {
    let lightItem = document.createElement('span');
    let deg = (360 / this.lightNum) * i
    lightItem.style.transform = `rotate(${deg}deg)`;
    lightFragment.appendChild(lightItem);
}
this.light.appendChild(lightFragment);
  • 接下來就是如何讓這些小圓點顿仇,顯示在“正確的位置上”

透露一下淘正,主要利用的是transform:rotate()transform-origin: center center這兩個屬性臼闻。

給 light 寬鸿吆、高和外層容器一致,里面添加元素述呐,而元素的寬度給和小圓點一致的寬度惩淳,而這些元素旋轉(zhuǎn)一定的角度((360 / this.lightNum) * i),而它們的旋轉(zhuǎn)中心設(shè)置在中心點乓搬。達(dá)到的效果就是這樣的:

接下來就是繪制小圓點了思犁,利用:before屬性,將小圓點繪制到頂部进肯。而閃爍的燈激蹲,有兩種顏色,再利用:nth-type-of(event/odd)實現(xiàn)江掩。最后就是閃爍了托呕,利用的是@keyframesanimation频敛。

最終實現(xiàn):

@keyframes white-to-yellow {
    0% {
      background: #fff;
    }
    100% {
      background: #d7a945;
    }
}

.turntable-wrap .light {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: #e0ddd1;
    animation: rotate 5s linear infinite;
}
.turntable-wrap .light span {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    margin: 0 auto;
    width: 10px;
    height: 100%;
    transform-origin: center center;
}
.turntable-wrap .light span:before {
    content: '';
    position: absolute;
    top: 5px;
    left: 0;
    right: 0;
    margin: 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
}
.turntable-wrap .light span:nth-of-type(even):before {
    background: #fff;
    animation: white-to-yellow 1s linear infinite;
}
.turntable-wrap .light span:nth-of-type(odd):before {
    background: #d7a945;
    animation: white-to-yellow 1s linear reverse infinite;
}

轉(zhuǎn)盤的背景

轉(zhuǎn)盤的背景的布局原理和燈的布局原理是一致的项郊。就不重復(fù)了。itemNum = 6斟赚,表示轉(zhuǎn)盤分6塊着降。

// 初始化轉(zhuǎn)盤背景
let bgFragment = document.createDocumentFragment();
for (let i = 0; i < this.itemNum; i++) {
    let bgItem = document.createElement('li');
    let deg = (360 / this.itemNum) * i
    bgItem.style.transform = `rotate(${deg}deg)`;
    bgFragment.appendChild(bgItem);
}
this.bg.appendChild(bgFragment);

主要的 css 如下:

.turntable-wrap .turntable .bg {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: #fff;
    border: 1px solid #dfd8be;
    border-radius: 50%;
    transform: rotate(90deg);
}
.turntable-wrap .turntable .bg li {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    margin: 0 auto;
    width: 1px;
    height: 100%;
    background: #dfd8be;
    transform-origin: center center;
}

效果圖:

轉(zhuǎn)盤的中獎圖

利用的也是transform:rotate()transform-origin屬性拗军,達(dá)到旋轉(zhuǎn)布局的任洞。但是又有一點點不一樣蓄喇。

為了讓中獎圖顯示在轉(zhuǎn)盤每個塊的正中間

所以每個塊是按transorm-origin:right bottom這個方向旋轉(zhuǎn)角度,而外層旋轉(zhuǎn)transform: rotate(45deg)交掏,里面的每個小圖旋轉(zhuǎn)transform: rotate(-45deg)再糾正過來妆偏。

ps:其實按照燈的transorm-origin:center center應(yīng)該也能實現(xiàn),但開始想到的就是這種方案盅弛。

js 初始化塊的元素代碼如下:

// 初始化轉(zhuǎn)盤上的中獎圖
let giftFragment = document.createDocumentFragment();
for (let i = 0; i < this.itemNum; i++) {
    let giftItem = document.createElement('li');
    giftItem.style.transform = `rotate(${deg}deg)`;
    giftItem.className = this.typeClassMap[this.lottery[i].type];
    let span = document.createElement('span');
    span.innerHTML = this.typeMap[this.lottery[i].type];
    giftItem.appendChild(span);
    giftFragment.appendChild(giftItem)
}
this.gift.appendChild(giftFragment);

其中元素span完全用來顯示具體內(nèi)容的钱骂,比如紅包。

typeMap: {1: '¥', 2: '謝謝參與'},
typeClassMap: {1: '', 2: 'no-gift'},

css 布局代碼如下:

.turntable-wrap .turntable .gift {
    position: relative;
    width: 100%;
    height: 100%;
    transform: rotate(45deg);
}
.turntable-wrap .turntable .gift li {
    position: absolute;
    top: 5%;
    left: 5%;
    width: 45%;
    height: 45%;
    transform-origin: right bottom;
}
.turntable-wrap .turntable .gift li span {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: block;
    width: 50px;
    height: 70px;
    margin: auto;
    background: yellow;
    transform: rotate(-45deg);
    text-align: center;
    line-height: 80px;
    border-radius: 5px;
    background: #f23c3c;
    color: #fff;
    font-size: 24px;
}
.turntable-wrap .turntable .gift li:not(.no-gift) span:before {
    content: '';
    position: absolute;
    top: 15px;
    left: 0;
    width: 50px;
    height: 1px;
    background: #fff;
}
.turntable-wrap .turntable .gift li.no-gift span {
    background: #fff;
    line-height: 70px;
    color: #bfa74f;
    font-size: 12px;
}

轉(zhuǎn)盤指針

其實這個還是比較簡單的挪鹏,利用border繪制的三角形见秽。

.turntable-wrap .pointer {
    box-sizing: border-box;
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    margin: -23px auto;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: #fff;
    border: 5px solid #fff;
    box-shadow: 0 0 0 5px #b9a046;
    text-align: center;
    line-height: 16px;
    color: #b9a046;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
}
.turntable-wrap .pointer:before {
    content: '';
    position: absolute;
    top: -58px;
    left: 0;
    right: 0;
    margin: 0 auto;
    width: 0;
    border-style: solid;
    border-color: transparent transparent #b9a046 transparent;
    border-width: 25px 10px 25px 10px;
}

原生 js 實現(xiàn)抽獎效果

主要代碼如下:

gameStart () {
  if (this.isGoing) {
    return
  }
  this.isGoing = true;

  // 1. 隨機(jī)中獎結(jié)果
  // 從1-100之間得到一個隨機(jī)數(shù),看這個隨機(jī)數(shù)在中獎設(shè)置的范圍讨盒,得到最終中獎的項
  let randomRate = ~~(Math.random() * 100) // ~~ == Math.floor()
  // 設(shè)置中獎數(shù)據(jù)的概率范圍
  let num = 0
  this.lottery.forEach(item => {
    item.min = num;
    num += item.rate;
    item.max = num;
  })
  // 根據(jù)隨機(jī)數(shù)解取,得到中獎結(jié)果
  let res = this.lottery.filter(item => {
    return randomRate >= item.min && randomRate < item.max;
  })[0];
  // 這兒可以根據(jù)實際情況,可重置中獎結(jié)果
  
  // 2. 計算旋轉(zhuǎn)角度, 需要多轉(zhuǎn)5圈返顺,達(dá)轉(zhuǎn)1圈用時1s, 到旋轉(zhuǎn)的效果
  let rotateItemDeg = (res.location - 1) * (360 / this.lottery.length); // 每個item旋轉(zhuǎn)角度, 第一個不用旋轉(zhuǎn)
  let rotate = rotateItemDeg + 5 * 360;
  let rotateSpeed = (rotateItemDeg / 360 * 1 + 5).toFixed(2);
  // 重置轉(zhuǎn)盤樣式
  this.turntable.removeAttribute('style');
  // 保證下一次旋轉(zhuǎn)動畫生效
  setTimeout(() => {
    this.turntable.style.transform = `rotate(${rotate}deg)`;
    this.turntable.style.transition = `transform ${rotateSpeed}s ease-out`;
  }, 10)

  // 3. 動畫結(jié)束禀苦,顯示中獎結(jié)果,中獎結(jié)果如何顯示遂鹊,視實際情況而定
  setTimeout(() => {
    this.isGoing = false;
    console.log('中獎結(jié)果:', randomRate, res, this.typeMap[res.type]);
  }, rotateSpeed * 1000);
}

由于代碼中的注釋還是比較全的振乏,就不贅述了。

以下是中獎結(jié)果的配置稿辙,實際中,可通過接口獲取气忠。rate 是中獎比例的控制邻储。

 let lottery = [
    {
      location: 1, // 位置
      type: 1, // 中獎
      rate: 30, // 中獎比例 1-100
    },
    { 
      location: 2,
      type: 2, // 未中獎
      rate: 20
    },
    { location: 3, type: 1, rate: 10 },
    { location: 4, type: 2, rate: 20 },
    { location: 5, type: 1, rate: 10 },
    { location: 6, type: 2, rate: 10 }
];

最后的最后

  • 文字描述的可能不是很清晰,可看github上的源碼實現(xiàn)旧噪。也用 vue吨娜、react實現(xiàn)了一遍效果,其中 vue 版本的是已經(jīng)運用于實際中的淘钟。
  • 實現(xiàn)方案可能不是最佳的宦赠,有更好方案的可提供建議。_
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末米母,一起剝皮案震驚了整個濱河市勾扭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铁瞒,老刑警劉巖妙色,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異慧耍,居然都是意外死亡身辨,警方通過查閱死者的電腦和手機(jī)丐谋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煌珊,“玉大人号俐,你說我怎么就攤上這事《ㄢ郑” “怎么了吏饿?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長洗贰。 經(jīng)常有香客問我找岖,道長,這世上最難降的妖魔是什么敛滋? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任许布,我火速辦了婚禮,結(jié)果婚禮上绎晃,老公的妹妹穿的比我還像新娘蜜唾。我一直安慰自己,他們只是感情好庶艾,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布袁余。 她就那樣靜靜地躺著,像睡著了一般咱揍。 火紅的嫁衣襯著肌膚如雪颖榜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天煤裙,我揣著相機(jī)與錄音掩完,去河邊找鬼。 笑死硼砰,一個胖子當(dāng)著我的面吹牛且蓬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播题翰,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼恶阴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了豹障?” 一聲冷哼從身側(cè)響起冯事,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎血公,沒想到半個月后桅咆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡坞笙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年岩饼,在試婚紗的時候發(fā)現(xiàn)自己被綠了荚虚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡籍茧,死狀恐怖版述,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寞冯,我是刑警寧澤渴析,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站吮龄,受9級特大地震影響俭茧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜漓帚,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一母债、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尝抖,春花似錦毡们、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至搅荞,卻和暖如春红氯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咕痛。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工痢甘, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暇检。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓产阱,卻偏偏與公主長得像婉称,于是被迫代替她去往敵國和親块仆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 各種純css圖標(biāo) CSS3可以實現(xiàn)很多漂亮的圖形王暗,我收集了32種圖形悔据,在下面列出。直接用CSS3畫出這些圖形俗壹,要比...
    劍殘閱讀 9,518評論 0 8
  • 在介紹有關(guān)transform相關(guān)的知識之前科汗,先來講一下transform-origin的用法以及關(guān)于角度的幾種取值...
    跪鍵盤的小泰迪閱讀 1,219評論 0 2
  • 1 CSS屬性 1.1 濾鏡 1.1.1 blur屬性 1.1.1.1 代碼示例 CSS代碼: .blur { ...
    Kevin_Junbaozi閱讀 726評論 1 4
  • 選擇qi:是表達(dá)式 標(biāo)簽選擇器 類選擇器 屬性選擇器 繼承屬性: color,font绷雏,text-align头滔,li...
    love2013閱讀 2,306評論 0 11
  • 堯在位七十多年怖亭,想找一個合適的繼承人。 他的兒子丹朱聰明伶俐坤检,力量驚人兴猩,但行事魯莽,好吃懶做早歇,爭強(qiáng)好勝倾芝,難以服眾。...
    楚慕閱讀 1,724評論 0 1