Ежедневные призы[by Власть (Wanderer)]

WandererOd

Новичок
Репутация
0 / 6
Хочу предложить модуль ежедневных призов.
В базе данных создать поле "present" типа int по умолчанию ставим 0.

Создаем БД запросом
SQL:
CREATE TABLE `presentDay` (
  `id` int NOT NULL,
  `login` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `stop` tinyint(1) NOT NULL DEFAULT '0',
  `0` tinyint NOT NULL,
  `1` tinyint NOT NULL,
  `2` tinyint NOT NULL,
  `3` tinyint NOT NULL,
  `4` tinyint NOT NULL,
  `5` tinyint NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;


--
ALTER TABLE `presentDay`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `presentDay`
  MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;
COMMIT;

Файл presents.php в корке сайта
PHP:
<?
require_once($_SERVER['DOCUMENT_ROOT'].'/inc/db_connect.php');

$title = 'Подарок';
include($_SERVER['DOCUMENT_ROOT']."/int/header.php");
?>
<link rel="stylesheet" type="text/css" href="/img/css/presents.css?v2">
<p style="text-align: center;">
  <input type="button" class="input" value="Назад" onclick="window.location.href=`main.php`">
</p>
<div class="body">
  <div class="deal-wheel">
    <ul class="spinner"></ul>
    <div class="ticker"></div>
    <?
    if($stat["present"] > time())
      echo'<button class="btn-spin" disabled>Раз в сутки</button>';
    else
      echo'<button class="btn-spin">Испытай удачу</button>';
    
    ?>
    
  </div>
  <script>
    const wheel = document.querySelector(".deal-wheel");
    const spinner = wheel.querySelector(".spinner");
    const trigger = wheel.querySelector(".btn-spin");
    const ticker = wheel.querySelector(".ticker");

    let prizes = [];
    let prizeSlice = 0;
    let prizeNodes;
    let rotation = 0;
    let currentSlice = 0;
    let tickerAnim;

    // Загружаем призы при старте
    window.addEventListener("DOMContentLoaded", async () => {
      const response = await fetch("getPresents.php?action=load");
      const data = await response.json();

      prizes = data.prizes;
      drawWheel();
    });

    function drawWheel() {
      prizeSlice = 360 / prizes.length;
      const prizeOffset = Math.floor(180 / prizes.length);

      spinner.innerHTML = '';
      spinner.setAttribute("style", `background: conic-gradient(
        from -90deg,
        ${prizes
          .map(({ color }, i) => `${color} 0 ${(100 / prizes.length) * (prizes.length - i)}%`)
          .reverse()
        });`
      );

      prizes.forEach(({ text }, i) => {
        const rotation = ((prizeSlice * i) * -1) - prizeOffset;
        spinner.insertAdjacentHTML(
          "beforeend",
          `<li class="prize" style="--rotate: ${rotation}deg">
            <span class="text">${text}</span>
          </li>`
        );
      });

      prizeNodes = wheel.querySelectorAll(".prize");
    }

    function runTickerAnimation() {
      const values = getComputedStyle(spinner).transform.split("(")[1].split(")")[0].split(",");
      const a = values[0], b = values[1];
      let rad = Math.atan2(b, a);
      if (rad < 0) rad += (2 * Math.PI);
      const angle = Math.round(rad * (180 / Math.PI));
      const slice = Math.floor(angle / prizeSlice);
      if (currentSlice !== slice) {
        ticker.style.animation = "none";
        setTimeout(() => ticker.style.animation = null, 10);
        currentSlice = slice;
      }
      tickerAnim = requestAnimationFrame(runTickerAnimation);
    }

    trigger.addEventListener("click", async () => {
      trigger.disabled = true;

      const response = await fetch("getPresents.php?action=spin");
      const data = await response.json();

      rotation = data.winnerIndex;
      trigger.disabled = true;
      trigger.innerHTML = data.btn;

      prizeNodes.forEach(p => p.classList.remove("selected"));
      wheel.classList.add("is-spinning");
      spinner.style.setProperty("--rotate", rotation);
      ticker.style.animation = "none";
      runTickerAnimation();
    });

    spinner.addEventListener("transitionend", () => {
      cancelAnimationFrame(tickerAnim);
      rotation %= 360;
      wheel.classList.remove("is-spinning");
      spinner.style.setProperty("--rotate", rotation);

      const selected = Math.floor(rotation / prizeSlice);
      prizeNodes[selected].classList.add("selected");
    });
  </script>
</div>

Файл стилей
CSS:
.input {
  width: 250px;
  background-color: #691d05;
  border-radius: 5px;
  border: 1px solid #aaa;
  color: cornsilk;
  font-size: 12px;
  padding: 3px;
}

.input:focus, .input:hover {
  background-color: #613122;
  color: #e5d597;
  box-shadow: 0 0 5px rgba(255,25,33,.9);
  -moz-box-shadow:0 0 5px rgba(255,25,33,.9);
  -webkit-box-shadow:0 0 5px rgba(255,25,33,.9);
}

.input:disabled {
  background-color: #4f3127;
  color: #e5d597;
  box-shadow: 0 0 5px rgba(0,0,0,.9);
  -moz-box-shadow:0 0 5px rgba(0,0,0,.9);
  -webkit-box-shadow:0 0 5px rgba(0,0,0,.9);
  cursor: no-drop;
}

.input:disabled {
  background-color: #4f3127;
  color: #e5d597;
  box-shadow: 0 0 5px rgba(0,0,0,.9);
  -moz-box-shadow:0 0 5px rgba(0,0,0,.9);
  -webkit-box-shadow:0 0 5px rgba(0,0,0,.9);
  cursor: no-drop;
}

.input::placeholder {
  color: cornsilk;
  opacity: 1;
  font-style: italic;
  font-size: 12px;
}

.input::-moz-placeholder {
  color: cornsilk;
  opacity: 1;
  font-style: italic;
  font-size: 12px;
}

.input::-webkit-input-placeholder {
  color: cornsilk;
  opacity: 1;
  font-style: italic;
  font-size: 12px;
}



/* общий блок для всех элементов */
.deal-wheel {
  /* задаём переменные блока */
  /* размеры колеса */
  --size: clamp(250px, 80vmin, 700px);
  /* настройки яркости и заливки фона секторов */
  --lg-hs: 0 3%;
  --lg-stop: 50%;
  --lg: linear-gradient(
      hsl(var(--lg-hs) 0%) 0 var(--lg-stop),
      hsl(var(--lg-hs) 20%) var(--lg-stop) 100%
    );
  /* добавляем позиционирование относительно других элементов */
  position: relative;
  /* подключаем сетку */
  display: grid;
  grid-gap: calc(var(--size) / 20);
  /* выравниваем содержимое блока по центру */
  align-items: center;
  /* задаём имена областей внутри сетки */
  grid-template-areas:
    "spinner"
    "trigger";
  /* устанавливаем размер шрифта */
  font-size: calc(var(--size) / 21);
}

/* всё, что относится ко внутренним элементам главного блока, будет находиться в области сетки с названием spinner */
.deal-wheel > * {
  grid-area: spinner;
}

/* сам блок и кнопка будут находиться в области сетки с названием trigger и будут выровнены по центру */
.deal-wheel .btn-spin {
  grid-area: trigger;
  justify-self: center;
}

/* сектор колеса */
.spinner {
  /* добавляем относительное позиционирование */
  position: relative;
  /* подключаем сетку */
  display: grid;
  /* выравниваем всё по центру */
  align-items: center;
  /* добавляем элемент в сетку */
  grid-template-areas: "spinner";
  /* устанавливаем размеры */
  width: var(--size);
  height: var(--size);
  /* поворачиваем элемент  */
  transform: rotate(calc(var(--rotate, 25) * 1deg));
  /* рисуем круглую обводку, а всё, что не поместится, — будет скрыто за кругом */
  border-radius: 50%;
}

/* всё, что внутри этого блока, будет находиться в области сетки с названием spinner */
.spinner * {
  grid-area: spinner;
}

/* текст на секторах */
.prize {
  /* включаем «гибкую» вёрстку */
  display: flex;
  align-items: center;
  /* задаём отступы от краёв блока */
  padding: 0 calc(var(--size) / 6) 0 calc(var(--size) / 20);
  /* устанавливаем размеры */
  width: 50%;
  height: 50%;
  /* устанавливаем координаты, относительно которых будем вращать текст */
  transform-origin: center right;
  /* поворачиваем текст */
  transform: rotate(var(--rotate));
  /* запрещаем пользователю выделять мышкой текст на секторах */
  user-select: none;
}

/* язычок */
.ticker {
  /* добавляем относительное позиционирование */
  position: relative;
  /* устанавливаем размеры */
  left: calc(var(--size) / -15);
  width: calc(var(--size) / 10);
  height: calc(var(--size) / 20);
  /* фон язычка */
  background: var(--lg);
  /* делаем так, чтобы язычок был выше колеса */
  z-index: 1;
  /* форма язычка */
  clip-path: polygon(20% 0, 100% 50%, 20% 100%, 0% 50%);
  /* устанавливаем точку, относительно которой будет вращаться язычок при движении колеса */
  transform-origin: center left;
}

/* кнопка запуска колеса */
.btn-spin {
  color: white;
  background: black;
  border: none;
  /* берём размер шрифта такой же, как в колесе */
  font-size: inherit;
  /* добавляем отступы от текста внутри кнопки */
  padding: 0.9rem 2rem 1rem;
  /* скругляем углы */
  border-radius: 0.5rem;
  /* меняем внешний вид курсора над кнопкой на руку*/
  cursor: pointer;
}

/* если кнопка нажата и неактивна */
.btn-spin:disabled {
  /* меняем внешний вид курсора */
  cursor: progress;
  /* делаем кнопку полупрозрачной */
  opacity: 0.25;
}

/* анимация вращения */
.is-spinning .spinner {
  transition: transform 8s cubic-bezier(0.1, -0.01, 0, 1);
}

/* анимация движения язычка */
.is-spinning .ticker {
          animation: tick 700ms cubic-bezier(0.34, 1.56, 0.64, 1);
}


/* эффект, когда колесо задевает язычок при вращении */
@keyframes tick {
  40% {
    /* чуть поворачиваем язычок наверх в середине анимации */
    transform: rotate(-12deg);
  }
}

/* анимируем выпавший сектор */
.prize.selected .text {
  /* делаем текст белым */
  color: white;
  /* настраиваем длительность анимации */
  animation: selected 800ms ease;
}

/* настраиваем анимацию текста на выпавшем секторе по кадрам */
@keyframes selected {
  /* что происходит на 25% от начала анимации */
  25% {
    /* увеличиваем текст в 1,25 раза */
    transform: scale(1.25);
    /* добавляем тексту тень */
    text-shadow: 1vmin 1vmin 0 hsla(0 0% 0% / 0.1);
  }
  40% {
    transform: scale(0.92);
    text-shadow: 0 0 0 hsla(0 0% 0% / 0.2);
  }
  60% {
    transform: scale(1.02);
    text-shadow: 0.5vmin 0.5vmin 0 hsla(0 0% 0% / 0.1);
  }
  75% {
    transform: scale(0.98);
  }
  85% {
    transform: scale(1);
  }
}

Файл getPresents
Код:
<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/inc/db_connect.php');


$presentDay = mysql_query(false, "SELECT * FROM `presentDay` WHERE `login` = ? AND `stop` = 0", "s", [$stat['user']]);
if(mysql_num_rows($presentDay) == 0) {
  $presentDay = [rand(10, 14), rand(20, 30), rand(30, 40), rand(40, 50), rand(6, 9), rand(9, 12)];
  mysql_query("INSERT INTO `presentDay` (`login`, `0`, `1`, `2`, `3`, `4`, `5`) VALUES ('".$stat['user']."', ".$presentDay[0].", ".$presentDay[1].", ".$presentDay[2].", ".$presentDay[3].", ".$presentDay[4].", ".$presentDay[5].")");
}
else
  $presentDay = mysql_fetch_array($presentDay);

header('Content-Type: application/json');

$action = $_GET['action'] ?? 'load';
$prizes = [
  ["price" => $presentDay[0], "text" => $presentDay[0].' зм', "color" => "#f44336", "weight" => 30],
  ["price" => $presentDay[1], "text" => $presentDay[1].' зм', "color" => "#e91e63", "weight" => 25],
  ["price" => $presentDay[2], "text" => $presentDay[2].' зм', "color" => "#9c27b0", "weight" => 20],
  ["price" => $presentDay[3], "text" => $presentDay[3].' зм', "color" => "#3f51b5", "weight" => 15],
  ["price" => $presentDay[4], "text" => $presentDay[4].' сз', "color" => "#2196f3", "weight" => 5],
  ["price" => $presentDay[5], "text" => $presentDay[5].' сз', "color" => "#009688", "weight" => 5],
];

if($action === 'load') {
  echo json_encode(["prizes" => $prizes]);
  exit;
}

if($action === 'spin') {
  if($stat["present"] > time()) {
    echo json_encode(["btn" => 'Раз в сутки', "winnerIndex" => 0]);
    exit; 
  }
 
  $weights = array_column($prizes, 'weight');
  $totalWeight = array_sum($weights);
  $rand = mt_rand(1, $totalWeight);

  $cumulative = 0;
  $winnerIndex = 0;
  foreach ($weights as $i => $weight) {
    $cumulative += $weight;
    if ($rand <= $cumulative) {
      $winnerIndex = $i;
      break;
    }
  }
  mysql_query("UPDATE `presentDay` set `stop` = 1 WHERE `login` = '".$stat['user']."' AND `stop` = 0");
  if($winnerIndex <= 3)
    mysql_query("UPDATE `players` set `credits` = `credits` + ".$prizes[$winnerIndex]['price'].", `present` = ".(time() + 86400)." WHERE `id` = ".$stat['id']."");
  else
    mysql_query("UPDATE `players` set `slit` = `slit` + ".$prizes[$winnerIndex]['price'].", `present` = ".(time() + 86400)." WHERE `id` = ".$stat['id']."");
 

  echo json_encode(["btn" => 'Раз в сутки', "winnerIndex" => $winnerIndex * 60 + rand(6, 16) * 360 + rand(20, 40)]);
  exit;
}
?>



По настройкам
$presentDay = [rand(10, 14), rand(20, 30), rand(30, 40), rand(40, 50), rand(6, 9), rand(9, 12)]; рандомное количество призов. первые 4 это в обычной валюте. остальные в сз. это можно изменить в $winnerIndex <= 3.

Все выглядит в виде "колеса фортуны"
 

Похожие темы

Сверху