Skip to content

Commit

Permalink
feat: sliding animation working but with flicker
Browse files Browse the repository at this point in the history
  • Loading branch information
WarFox committed Jan 1, 2025
1 parent e6b0cef commit f04943a
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 204 deletions.
81 changes: 33 additions & 48 deletions src/cljs_2048/animation.cljs
Original file line number Diff line number Diff line change
@@ -1,55 +1,40 @@
(ns cljs-2048.animation
(:require [cljs-2048.board :as b]
[cljs.spec.alpha :as s]))
(:require [cljs-2048.game :as g]))

(def board-size 4) ; Add constant for magic number 4

(s/def ::position (s/tuple int? int?))
(s/def ::moves (s/map-of ::position ::position))
(defn get-transition-style
"Return --move-x and --move-y based on direction and row/col
Move must be within bounds of the grid with 4 rows and 4 columns.
Maximum move is 3 rows or columns in any direction.
If col is 0, and direction is right, we can move 3 more columns to the right.
If col is 1, and direction is right, we can move 2 more columns to the right.
If col is 2, and direction is right, we can move 1 more columns to the right.
If col is 3, and direction is right, we can move 0 more columns to the right.
(defn board->tile-positions
"Creates a map of positions to tile values, excluding empty tiles"
[board]
(->> (for [row (range board-size)
col (range board-size)
:let [tile (b/get-tile board row col)]
:when (not= [0] tile)]
[[row col] (first tile)])
(into {})))
If col is 0, and direction is left, we can move 0 more columns to the left.
If col is 1, and direction is left, we can move 1 more columns to the left.
If col is 2, and direction is left, we can move 2 more columns to the left.
If col is 3, and direction is left, we can move 3 more columns to the left.
(defn find-tile-destination
"Find the destination position for a tile value in the new board.
Returns [row col] of destination or nil if no move."
[value old-pos new-board]
(->> (for [row (range board-size)
col (range board-size)
:let [new-val (first (b/get-tile new-board row col))]
:when (and (not= [row col] old-pos)
(or (= new-val value)
(= new-val (* 2 value))))]
[row col])
first))
If row is 0, and direction is down, we can move 3 more rows down.
(defn calculate-moves
"Calculate the moves for each tile from old board to new board.
Returns a map of {[old-row old-col] [new-row new-col]} for each moved tile."
[old-board new-board]
(if (= old-board new-board)
{}
(let [old-positions (board->tile-positions old-board)]
(->> old-positions
(keep (fn [[old-pos value]]
(when-let [new-pos (find-tile-destination value old-pos new-board)]
[old-pos new-pos])))
(into {})))))
If row is 3, and direction is up, we can move 3 more rows up.
(defn get-transition-class
"Get the CSS class for tile position"
[[row col]]
(str "translate-x-" (* col 100) "% translate-y-" (* row 100) "%"))
We multiply by 8em to match the tile size in the css grid."
[[row col] direction]
(let [max-moves 3
[move-x move-y] (case direction
::g/right (if (< col max-moves)
[(- max-moves col) 0]
[0 0])
::g/left (if (pos? col)
[(- col) 0]
[0 0])
::g/down (if (< row max-moves)
[0 (- max-moves row)]
[0 0])
::g/up (if (pos? row)
[0 (- row)]
[0 0]))]

(defn get-transition-style
"Get the CSS class for tile position"
[[old-row old-col] [new-row new-col]]
{"--move-x" (str (* (- new-row old-row) 8) "em")
"--move-y" (str (* (- new-col old-col) 8) "em")})
{"--move-x" (str (* move-x 8) "em")
"--move-y" (str (* move-y 8) "em")}))
3 changes: 3 additions & 0 deletions src/cljs_2048/db.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@
{:name "2048"
:score 0
:high-score (ls/get-high-score)
:sliding? false
:score-changed false
:high-score-changed false
:board board/initial-board})
57 changes: 31 additions & 26 deletions src/cljs_2048/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@
[cljs-2048.game :as g]
[cljs-2048.local-storage :as ls]
[day8.re-frame.tracing :refer-macros [fn-traced]]
[re-frame.core :as re-frame]
[cljs-2048.animation :as animation]))
[re-frame.core :as re-frame]))

;; DB events

(re-frame/reg-event-db
::initialize-db
(fn-traced
[_ _]
db/default-db))
::initialize-db
(fn-traced
[_ _]
db/default-db))

(defn start-game
[db [_ _]]
Expand All @@ -25,13 +24,6 @@

(re-frame/reg-event-db ::start-game start-game)

(defn clear-tile-moves
[db [_ _]]
(assoc db
:tile-moves {}))

(re-frame/reg-event-db ::clear-tile-moves clear-tile-moves)

(defn gameover
[db [_]]
(assoc db :gameover true))
Expand All @@ -46,6 +38,19 @@

(re-frame/reg-event-db ::clear-score-changes clear-score-changes)

(defn clear-animation
[db _]
(assoc db
:sliding? false))

(re-frame/reg-event-db ::clear-animation clear-animation)

(defn swap-new-board
[db [_ new-board]]
(assoc db :board new-board))

(re-frame/reg-event-db ::swap-new-board swap-new-board)

;; FX events

(defn add-score
Expand All @@ -58,24 +63,24 @@
:high-score high-score
:score-changed true
:high-score-changed (> high-score (:high-score db)))
:fx [[:dispatch-later {:ms 300 :dispatch [::clear-score-changes]}]]}))
:fx [[:dispatch-later {:ms 200 :dispatch [::clear-score-changes]}]]}))

(re-frame/reg-event-fx ::add-score add-score)

(defn move
[{:keys [db]} [_ direction]]
(let [board (:board db)
new-board (g/move (:board db) direction)
tile-moves (animation/calculate-moves board new-board)]
(let [board (:board db)
new-board (g/move board direction)]
{:db (assoc db
:board new-board
:tile-moves tile-moves)
:fx (if (g/gameover? new-board)
[[:dispatch [::gameover]]]

[[:dispatch-later {:ms 300 :dispatch [::clear-tile-moves]}]
(let [score (g/move-score new-board)]
(when (pos? score)
[:dispatch [::add-score score]]))])}))
:direction direction
:sliding? (not (b/equal? board new-board)))
:fx (if (g/gameover? new-board)
[[:dispatch [::gameover]]]

[[:dispatch-later {:ms 200 :dispatch [::clear-animation]}]
[:dispatch-later {:ms 200 :dispatch [::swap-new-board new-board]}]
(let [score (g/move-score new-board)]
(when (pos? score)
[:dispatch [::add-score score]]))])}))

(re-frame/reg-event-fx ::move move)
4 changes: 2 additions & 2 deletions src/cljs_2048/game.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
(->> board
(mapcat (fn [row]
(filter (fn [tile]
(= :merged (second tile))) row)))
(= :merged (second tile))) row)))
(map first)
(reduce + 0)))

Expand All @@ -79,5 +79,5 @@
[board direction]
(let [new-board ((get movements direction) board)]
(if (b/equal? new-board board)
new-board
board
(b/random-tile new-board))))
9 changes: 7 additions & 2 deletions src/cljs_2048/subs.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
(:high-score-changed db)))

(re-frame/reg-sub
::tile-moves
::direction
(fn [db _]
(:tile-moves db)))
(:direction db)))

(re-frame/reg-sub
::sliding?
(fn [db _]
(:sliding? db)))
25 changes: 10 additions & 15 deletions src/cljs_2048/views.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -56,39 +56,34 @@

(defn tile
"Displaying the game tile"
[row-index col-index [value state]]
(let [tile-moves @(re-frame/subscribe [::subs/tile-moves])
new-position (get tile-moves [row-index col-index])
transition-style (animation/get-transition-style
[row-index col-index]
(if new-position
new-position
[row-index col-index]))]
^{:key (str row-index col-index)} ;; unique identifier
[:div {:class ["tile relative"
(if new-position "tile-slide z-30" "z-20")
[sliding? direction row-index col-index [value state]]
^{:key (str row-index col-index)} ;; unique identifier
[:div {:class ["tile relative z-20"
(when sliding? "tile-slide")
(when (= state :merged) "tile-merged")
(when (= state :random) "tile-new")]
:role "gridcell"
:aria-label (str "Tile " value)
:tabIndex "0"
:style transition-style}
:style (when sliding? (animation/get-transition-style [row-index col-index] direction))}
[:div {:class ["flex justify-center items-center rounded-md size-28" (str "tile-" value)]}
(when-not (zero? value)
[:span {:class "text-5xl font-bold"} value])]]))
[:span {:class "text-5xl font-bold"} value])]])

(defn board-panel
"Rows and columns display of current board"
[]
(let [board @(re-frame/subscribe [::subs/board])] ;; Get the current state of the board
(let [board @(re-frame/subscribe [::subs/board]) ;; Get the current state of the board
sliding? @(re-frame/subscribe [::subs/sliding?])
direction @(re-frame/subscribe [::subs/direction])]
;; absolute and z-10 to make the board on top of the grid
[:div {:class "absolute z-10 grid grid-rows-4 grid-cols-4 gap-4 p-4" :id "board-panel"}
;; [:pre (with-out-str (cljs.pprint/pprint @board))] ;; print the board in page for debugging
(map-indexed
(fn [row-index row] ;; Each row of the board
(map-indexed
(fn [col-index cell]
(tile row-index col-index cell))
(tile sliding? direction row-index col-index cell))
row))
board)]))

Expand Down
18 changes: 2 additions & 16 deletions src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -164,22 +164,8 @@
}
}

/** New **/

/** tile slide animation **/
/*
@keyframes slide {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(var(--move-x), var(--move-y));
}
} */

/* Tile slide animation */
.tile-slide {
/* animation: slide 500ms ease-in; */
transition: transform 500ms ease;
transition: transform 200ms ease;
transform: translate(var(--move-x), var(--move-y));
}
Loading

0 comments on commit f04943a

Please sign in to comment.