<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stéphane CHANGARNIER</title>
    <description>The latest articles on DEV Community by Stéphane CHANGARNIER (@lansolo99).</description>
    <link>https://dev.to/lansolo99</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F124874%2F7d86425f-a253-4c2a-b471-80a360dcdd2b.jpeg</url>
      <title>DEV Community: Stéphane CHANGARNIER</title>
      <link>https://dev.to/lansolo99</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lansolo99"/>
    <language>en</language>
    <item>
      <title>Building a narrative game with Next.js and AI</title>
      <dc:creator>Stéphane CHANGARNIER</dc:creator>
      <pubDate>Thu, 20 Mar 2025 13:31:38 +0000</pubDate>
      <link>https://dev.to/lansolo99/building-a-narrative-game-with-nextjs-and-ai-4mk0</link>
      <guid>https://dev.to/lansolo99/building-a-narrative-game-with-nextjs-and-ai-4mk0</guid>
      <description>&lt;p&gt;Note: This article was originally published on my &lt;a href="https://lansolo.dev/posts/building-a-narrative-game-whith-nextjs-and-ai" rel="noopener noreferrer"&gt;technical blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;"&lt;a href="https://lansolo.dev/posts/building-a-food-simulation-game-with-nextjs-and-ai" rel="noopener noreferrer"&gt;Noël Solidaire 2024&lt;/a&gt;", the previous game release, was an opportunity to experiment with generative images to create a custom background set based on a specific style.&lt;/p&gt;

&lt;p&gt;This time, I wanted to dig deeper into those restyling capabilities to re-style some Montpellier places, my home base.&lt;/p&gt;

&lt;p&gt;I imagined a small narrative game that would make players discover the city through the transportation network.&lt;/p&gt;

&lt;p&gt;I then quickly elaborated a scenario of a student's journey that would have to travel the city to reach a train station through a commute transit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sets
&lt;/h2&gt;

&lt;p&gt;At the beginning, I decided on which places would be seen and how a transit system could work. I selected some key tram stations with connections as stops.&lt;/p&gt;

&lt;p&gt;Then I took some screenshots using Google Street View of each place.&lt;/p&gt;

&lt;p&gt;Finally I used &lt;a href="https://www.dzine.ai/" rel="noopener noreferrer"&gt;Dzine&lt;/a&gt; once again to restyle each screenshot using a personal model style. This style was computed from a bunch of artworks I selected for their artistic styles.&lt;/p&gt;

&lt;p&gt;Often, getting the proper result needed multiple generations with revised prompts.&lt;/p&gt;

&lt;p&gt;I got about 50 generated sets to cover the whole story.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-narrative-game-with-nextjs-and-ai-dzine.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-narrative-game-with-nextjs-and-ai-dzine.jpg" alt="Dzine - Montpellier Scene" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sound
&lt;/h2&gt;

&lt;p&gt;The effects come from a sound library, however, the music is generated using &lt;a href="https://soundraw.io/" rel="noopener noreferrer"&gt;Soundraw&lt;/a&gt;. I didn't spend a lot of time because I found the first generation satisfying.&lt;/p&gt;

&lt;h2&gt;
  
  
  Story
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dialogs
&lt;/h3&gt;

&lt;p&gt;I started by gathering dialog ideas, then sorted them as dialog descriptions for each sentence. As I don't have any storywriting skills, I once again relied on AI to generate my dialogs based on the desired style, game context, scenes, and dialog descriptions using a mix of Perplexity, ChatGPT, and Mistral AI.&lt;/p&gt;

&lt;p&gt;I found myself rewriting a lot of dialogs, but was surprised at how well they were globally crafted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nodes
&lt;/h3&gt;

&lt;p&gt;Once all dialogs were ready, I needed a nodal system to organize dialogs for each scene. Instead of directly crafting a huge JSON object, I relied on a visual tool called &lt;a href="https://twinery.org/" rel="noopener noreferrer"&gt;Twine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Twine is both a web tool and a desktop app that is assisting through the creation on non-linear stories with links between story nodes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-narrative-game-with-nextjs-and-ai-twine.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-narrative-game-with-nextjs-and-ai-twine.jpg" alt="Twine - story nodes" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the nodes were crafted, we could simply export the story as a single HTML file.&lt;/p&gt;

&lt;p&gt;The next step was to parse this file so that it could be handled by our Next.js app.&lt;/p&gt;

&lt;p&gt;It was then just a matter of displaying the proper scene and dialogs using some states and logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;As always, I kept using Next.js, Tailwind, and Framer Motion for development, and Figma for UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;The only workflow change was switching IDEs, from VSCode/Copilot to &lt;a href="https://codeium.com/" rel="noopener noreferrer"&gt;Windsurf/Cascade&lt;/a&gt;, as it was depicted as a more performant AI regarding the app context.&lt;/p&gt;

&lt;p&gt;Overall, my experience working with Windsurf was positive, and I indeed found Cascade (the chat assistant) very clever regarding my global app context and what I was trying to achieve.&lt;/p&gt;

&lt;p&gt;The Cascade "write" mode generated nearly 75% of my logic code, getting me into this "vibe coding" workflow.&lt;/p&gt;

&lt;p&gt;I encountered some struggle though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It messed up with some existing code, and I had to carefully create rules to avoid that&lt;/li&gt;
&lt;li&gt;It often happened that Cascade entered an infinite change loop, iterating on itself without end. The only workaround was to restart Cascade, improve prompts, and be clear about doing simple changes one by one.&lt;/li&gt;
&lt;li&gt;Files are getting bulkier as time goes by, as Cascade tends to aggregate dozens of functions in a single component file. We have to refactor explicitly and thoroughly retest the app afterwards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of problems are avoided by creating detailed and explicit &lt;a href="https://docs.codeium.com/windsurf/memories" rel="noopener noreferrer"&gt;Cascade rules&lt;/a&gt; and also what is called "memories".&lt;/p&gt;

&lt;p&gt;The pricing is pretty affordable ($15/monthly) for the time-saving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The creative phase took about 1-2 weeks (brainstorming + visual creations) and the development phase took about 1 week.&lt;/p&gt;

&lt;p&gt;This was a very pleasant and instructive way to develop the game using Cascade.&lt;/p&gt;

&lt;p&gt;I just have some thoughts about us getting more and more assisted with AI (design, ideas, architecting, coding). While we are shipping faster, it also tends to soften us intellectually.&lt;/p&gt;

&lt;p&gt;I don't know what to think at this stage. I just know things tend to evolve at an exponential speed these days...&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Here is the &lt;a href="https://correspondance-game.vercel.app/" rel="noopener noreferrer"&gt;playable game&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The game takes only 5-10 minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-narrative-game-with-nextjs-and-ai-ingame.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-narrative-game-with-nextjs-and-ai-ingame.jpg" alt="Ingame scenes" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's in French. &lt;br&gt;
"Correspondance" means "transit connection".&lt;/p&gt;

</description>
      <category>game</category>
      <category>nextjs</category>
      <category>windsurf</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Building a food simulation game with Next.js and generative design</title>
      <dc:creator>Stéphane CHANGARNIER</dc:creator>
      <pubDate>Sun, 08 Dec 2024 09:12:24 +0000</pubDate>
      <link>https://dev.to/lansolo99/building-a-food-simulation-game-with-nextjs-and-generative-design-31mm</link>
      <guid>https://dev.to/lansolo99/building-a-food-simulation-game-with-nextjs-and-generative-design-31mm</guid>
      <description>&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;The end of 2024 is almost there, and I have been asked by a french Bank to build a casual game for Christmas, as I have now done several in the past.&lt;/p&gt;

&lt;p&gt;After we put ideas on the table, the one selected was a mini food simulation that fitted the most with the "charity" theme our client wanted to exploit.&lt;/p&gt;

&lt;p&gt;I benchmarked a bit and kept some gameboard designs as a starting to point to build a game scene showing a kitchen workshop with a specific illustration style (some outlined adorable characters and assets nested within an heart-warming environment).&lt;/p&gt;

&lt;h2&gt;
  
  
  Goal
&lt;/h2&gt;

&lt;p&gt;The game encourages the player to complete as many orders as possible within a given time. He will have to compose the proper food tray by picking up the right assets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generative design
&lt;/h2&gt;

&lt;p&gt;There was a problem: as being both in charge of the graphic design and the development, the chosen style was not in my skillset. The one I master is a low poly 3D isometric.&lt;/p&gt;

&lt;p&gt;It has been the opportunity to test a new creative workflow using AI tools to generate the needed environments in the specific style.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dzine.ai
&lt;/h3&gt;

&lt;p&gt;I tried some Stable Diffusion based tools, and the one I retained was &lt;a href="https://www.dzine.ai" rel="noopener noreferrer"&gt;Dzine.ai&lt;/a&gt;. An intuitive interface with predefined models and tools allowing a precise control over image content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sketch to image
&lt;/h3&gt;

&lt;p&gt;The most efficient starting point was to compose the game background on an existing image.&lt;br&gt;
Providing even an ugly sketch produce stunning results if accompagnied with a precise prompt.&lt;/p&gt;

&lt;p&gt;The initial concept was a gift workshop, and here was the result I got providing the following sketch and prompt:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-sketch2image.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-sketch2image.jpg" alt="Dzine - initial gift workshop scene" width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;The scene is a 2D flat, viewed from a frontal camera at eye level.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;It is a gift-making workshop in a cozy house.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;On the upper part of the image: the wall, with a cozy brick, a window with curtains and snowy outside. On the right part of the wall: a shelf with christmas decorations.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;On the lower part of the image: it's a workbench.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;There is a two-level workbench.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;On the higher level (a wooden table): some empty wooden boxes.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;On the lower level, beneath the table:&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;On its right side: three rolls of gift wrap in different colors.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;On its left side: three rolls of thin ribbons in different colors.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The overall ambiance is blue.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Image to image
&lt;/h3&gt;

&lt;p&gt;When it was decided to go with a food simulation, I used a screenshot of &lt;a href="https://play.google.com/store/apps/details?id=com.pixup.LilysCafe&amp;amp;hl=fr" rel="noopener noreferrer"&gt;Lili's Cafe&lt;/a&gt;, an existing mobile game I took as a model. Here are the result with a little refining through prompt:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-image2image.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-image2image.jpg" alt="Dzine - food workshop scene" width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Scene cleanup
&lt;/h3&gt;

&lt;p&gt;The next step was to get a clean separated background, so that I can manage assets apart. For this, I used both the Dzine "erase" tool, and the Photoshop "content aware fill" tool. Here is the evolution:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-erase-cleanup.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-erase-cleanup.jpg" alt="Dzine - erases" width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Assets
&lt;/h3&gt;

&lt;p&gt;At the beginning, I tried to populate the scene with assets using the "image-to-image" tool but the results were too weird.&lt;/p&gt;

&lt;p&gt;So I decided to use the text-to-image on a blank canvas for every single assets, but using a proper trained model (based on some uploaded Lili's Cafe images).&lt;/p&gt;

&lt;p&gt;I had to trigger a lot of generations for some, because it didn't match the result or angle I wanted.&lt;/p&gt;

&lt;p&gt;The final action were background removals and generations flaws fixs.&lt;/p&gt;

&lt;p&gt;Here are some examples:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-assets.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-assets.jpg" alt="Dzine assets generation - " width="800" height="753"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final gameboard
&lt;/h3&gt;

&lt;p&gt;I composed the final scene on Photoshop, importing and refining assets after assets. Finally, I set the game user interface with Figma. Here are the scene without and with assets + GUI:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-final.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-food-simulation-game-with-nextjs-and-ai-final.jpg" alt="final scene" width="800" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;Not that much to say, because I utilized my well-established following technology stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend: Next.js (v.15)&lt;/li&gt;
&lt;li&gt;Backend: Firestore (Firebase) as users database&lt;/li&gt;
&lt;li&gt;UI: Tailwind.css + ShadCn + Framer Motion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;The project is strongly based on our previous game releases (&lt;a href="https://lansolo.dev/posts/first-sveltkit-project-with-blackout" rel="noopener noreferrer"&gt;Blackout&lt;/a&gt;, &lt;a href="https://lansolo.dev/posts/pommeclic-fin-de-chantier-mini-game" rel="noopener noreferrer"&gt;Fin de Chantier&lt;/a&gt;, &lt;a href="https://lansolo.dev/posts/building-a-2D-runner-game-with-nextjs" rel="noopener noreferrer"&gt;Vendanges Master&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Therefore, I re-used the same architecture and patterns I had built for these past ones.&lt;/p&gt;

&lt;p&gt;I took advantage of the newly released "Copilot edits" feature that can simultaneously modify a set of files for us, based on a prompt. We just have to accept or discard the proposed solution.&lt;/p&gt;

&lt;p&gt;This has been helpful as we often have to change multiples files to implement a feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment(s)
&lt;/h2&gt;

&lt;p&gt;The project has to be deployed on an Apache server, so bye-bye "on-the-edge" Next.js toolings, such as Server Actions, or any Node-based features. It's all client-side static export.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;It would have been an usual work if I didn't experiment the generative image workflow.&lt;/p&gt;

&lt;p&gt;My opinion on this is still reserved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on the one hand, these tools give us the ability to produce a large variety of design without being an expert.&lt;/li&gt;
&lt;li&gt;On the other hand, editing generation is truly a pain in the a**.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are in the early stages of this technology and struggling with it is part of the game. &lt;/p&gt;

&lt;p&gt;But when I see how fast things are evolving, I think we can be confident that those struggles will tend to minimize as time goes by...&lt;/p&gt;

&lt;p&gt;For the time being, the best way would still be to hire an illustrator expert in this style.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Here is the &lt;a href="https://grand-jeu-noel-2024.vercel.app/jeu" rel="noopener noreferrer"&gt;demo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>framermotion</category>
      <category>dzine</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Building a mini casual game with Next.js</title>
      <dc:creator>Stéphane CHANGARNIER</dc:creator>
      <pubDate>Wed, 17 Jan 2024 14:40:00 +0000</pubDate>
      <link>https://dev.to/lansolo99/building-a-mini-casual-game-with-nextjs-1db8</link>
      <guid>https://dev.to/lansolo99/building-a-mini-casual-game-with-nextjs-1db8</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;&lt;em&gt;As a quick reminder&lt;/em&gt;&lt;/strong&gt;: this post is not a step-by-step tutorial on how I built this game, but just a review of how I approached this project during this journey.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The game
&lt;/h2&gt;

&lt;p&gt;First and foremost, here is the &lt;a href="https://findechantier.pommeclic.com/" rel="noopener noreferrer"&gt;playable live game&lt;/a&gt; this post is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goal
&lt;/h2&gt;

&lt;p&gt;I took this game as an opportunity to improve on my technical skill and some creative processes.&lt;br&gt;
We (with &lt;a href="https://pommeclic.com/" rel="noopener noreferrer"&gt;Pommeclic&lt;/a&gt;) decided to communicate early as a way to celebrate the new year by publicly offering a game contest with prizes to win.&lt;/p&gt;

&lt;h2&gt;
  
  
  Previously
&lt;/h2&gt;

&lt;p&gt;I didn't start from scratch. I previously released a pretty similar game one year ago called &lt;a href="https://blackout.pommeclic.com/" rel="noopener noreferrer"&gt;Blackout&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Ffirst-sveltkit-project-with-blackout.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Ffirst-sveltkit-project-with-blackout.jpg" alt="Blackout game"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The theme was the deep energy concern that occurs in Europe with the Ukrainian/Russian conflict.&lt;/p&gt;

&lt;p&gt;It takes the "whac a mole" concept and adapt it to electrical units to turn off as soon as they turn on.&lt;/p&gt;

&lt;p&gt;I decided to reuse most of the layout and principles in order to save time and focus more on the design and game logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  New Concept
&lt;/h2&gt;

&lt;p&gt;I kept the same isometric gameboard to support the action. After a bit, I was inspired by the story of the controversial &lt;a href="https://www.connexionfrance.com/article/French-news/Controversial-new-A69-motorway-in-southern-France-What-do-you-think" rel="noopener noreferrer"&gt;A69 highway&lt;/a&gt; construction.&lt;/p&gt;

&lt;p&gt;The idea is to prevent vehicles to arrive on site, to postpone the start as much as possible, so that I can keep the "whac a mole" principle, with moving target this time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-with-nextjs-zone-central.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-with-nextjs-zone-central.jpg" alt="Fin de chantier game - zone centrale"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;p&gt;I used Sveltekit for Blackout. Though I enjoyed its light syntax, I struggled with lacking features, especially having dynamic optimized assets. &lt;/p&gt;

&lt;p&gt;I also wanted to refocus on React through Nextjs, to avoid having an overly dispersed knowledge.&lt;/p&gt;

&lt;p&gt;I decided to go with the same stack regarding the rest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firebase as backend (auth + user data)&lt;/li&gt;
&lt;li&gt;Tailwind as UI framework&lt;/li&gt;
&lt;li&gt;Vercel as hosting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Environment
&lt;/h3&gt;

&lt;p&gt;I kept modeling using Cinema 4D to get the same low poly look &amp;amp; feel, this time designing a hilly countryside crossed by main roads, as well as tunnels. Those lanes will be the interactive part of the map.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-c4d-env.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-c4d-env.jpg" alt="Fin de chantier c4d gameboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then adapted predesigned construction vehicles, simplified and uniformized their styles. I rendered them in the 4 perspectives for which they will be used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-c4d-vehicles.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-c4d-vehicles.png" alt="Fin de chantier c4d vehicles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I defined the animated flags and smokes and rendered them as GIFs through After Effects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fv1705480693%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-smoke.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fv1705480693%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-smoke.gif" alt="Fin de chantier c4d smoke"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fv1705480693%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-flag.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fv1705480693%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-flag.gif" alt="Fin de chantier c4d flag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  UI
&lt;/h3&gt;

&lt;p&gt;As usual, I started with some Figma screens, based on the previously designed Blackout UI, and replace the environments. Overall, the UI stays the same. Some light improvements have been made here and there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-with-nextjs-figma.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-with-nextjs-figma.jpg" alt="Figma screens"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Migration from Svelte
&lt;/h3&gt;

&lt;p&gt;I undertook to migrate the most of my previous Svelte codebase into a NextJs one, using the now established app router. Most of the time, it was just a matter of mirroring structure, replace with the proper framework attributes syntax (&lt;code&gt;class=&lt;/code&gt; becomes &lt;code&gt;className=&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Though a lot of functional programing could stay the same, I had to reorganize how the data flows. Stores become separated contexts, and useState enter the game.&lt;/p&gt;

&lt;p&gt;I intended to experiment a lot with RSC and server actions, but, I almost any time gave up using the &lt;code&gt;"use-client"&lt;/code&gt; directive to be able to use freely all the UI mutation patterns I was used to. I really have to take the mental shift to de-correlate RSCs and CSCs.&lt;/p&gt;

&lt;p&gt;I guess it's no big deal as this app is mostly a client side with heavy reactive UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gameboard
&lt;/h3&gt;

&lt;p&gt;I used a single rendered image for the countryside background, and set the vehicles individually into their own components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-c4d-desktop-env.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-c4d-desktop-env.jpg" alt="Desktop c4d environment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main game logic consist on generating on the fly vehicles components based on an interval increasing in speed. Each vehicles take some props that define its vehicle's version to pick as well as a lane to run on.&lt;/p&gt;

&lt;p&gt;Vehicles animations are handled with Framer-Motion.&lt;/p&gt;

&lt;p&gt;I handled the vehicles masking (when entering tunnels) using a single css image-mask.&lt;/p&gt;

&lt;p&gt;For the rest, it's a matter of vehicles triggering their situation to a global game context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-context.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-context.jpg" alt="Fin de chantier React context"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This context is in charge of rendering the whole game status ('ready', 'gameover', 'in progress'...).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-gameboard-component.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Fbuilding-a-mini-casual-game-gameboard-component.jpg" alt="Fin de chantier Gameboard component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Next auth and Firebase
&lt;/h3&gt;

&lt;p&gt;I decided to go with Next Auth and found pretty easy to set it, but I didn't make the Firestore adapter work properly (to get the user data part of the Next Auth context).&lt;/p&gt;

&lt;p&gt;Instead, I juggled with both Next Auth and FireStore hooks to retrieve bound data given a user auth, when necessary, that is ugly and I'm well aware of it 🙁.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Copilot
&lt;/h2&gt;

&lt;p&gt;I didn't regret to get the Copilot subscription, that helped me in many ways. Sometimes, it misled me, especially providing obsolete import paths (from the Nextjs page router).&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Not that much to say with Vercel that do an excellent job and eases the process, be it the subdomain binding, preview deployments, error logs...&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;To conclude, and as always, working on this kind of side project has been very fun and enriching. Seeing the ideas come to life is very satisfying, while being able to deliver it within a limited time frame.&lt;/p&gt;

&lt;p&gt;It also help to underline which part of the process have to be improved, because I consider my codebase as pretty messy at the time, and that gives me a roadmap on what would be interesting to dig in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Here is the &lt;a href="https://findechantier.pommeclic.com/" rel="noopener noreferrer"&gt;playable game&lt;/a&gt; of "Fin de chantier" (french version only). The contest run until next February 12th. I will then reopen the game without the contest part.&lt;/p&gt;

&lt;p&gt;If interested, you can also play the still playable previous Svelte game called &lt;a href="https://blackout.pommeclic.com/" rel="noopener noreferrer"&gt;Blackout&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note
&lt;/h2&gt;

&lt;p&gt;This post is also part of my personal blog &lt;a href="https://lansolo.dev/" rel="noopener noreferrer"&gt;lansolo.dev&lt;/a&gt; where I share most of my experiments.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>framermotion</category>
      <category>webdev</category>
      <category>firebase</category>
    </item>
    <item>
      <title>A Tinder-like card game with Framer-Motion</title>
      <dc:creator>Stéphane CHANGARNIER</dc:creator>
      <pubDate>Tue, 26 Sep 2023 07:07:27 +0000</pubDate>
      <link>https://dev.to/lansolo99/a-tinder-like-card-game-with-framer-motion-35i5</link>
      <guid>https://dev.to/lansolo99/a-tinder-like-card-game-with-framer-motion-35i5</guid>
      <description>&lt;h2&gt;
  
  
  Stack used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js + Typescript&lt;/li&gt;
&lt;li&gt;Tailwind&lt;/li&gt;
&lt;li&gt;Framer Motion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Side note
&lt;/h2&gt;

&lt;p&gt;This post is not a tutorial on building this module from scratch, but a recap of the process and things I wanted to share. I invite you to take a look at the repo if you are interested on examining or pulling the &lt;a href="https://github.com/lansolo99/tinder-card-game-with-framer-motion" rel="noopener noreferrer"&gt;whole code&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;In the same vein of my precedent &lt;a href="https://dev.to/lansolo99/a-drag-and-drop-puzzle-game-with-framer-motion-4aon"&gt;small puzzle game&lt;/a&gt;, I adapted the concept of Tinder-like cards swipe using &lt;a href="https://www.framer.com/motion/" rel="noopener noreferrer"&gt;Framer-motion&lt;/a&gt;, a powerful React animation library that I still explore on a regular basis.&lt;/p&gt;

&lt;p&gt;Initially, I designed an &lt;a href="https://dribbble.com/shots/22512145-Tinder-inspired-eco-quiz" rel="noopener noreferrer"&gt;animated prototype&lt;/a&gt; and I decided to implement it, in order to customize it for client in the future.&lt;/p&gt;

&lt;p&gt;Here are the &lt;a href="https://tinder-card-game-with-framer-motion.vercel.app/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; and the &lt;a href="https://github.com/lansolo99/tinder-card-game-with-framer-motion" rel="noopener noreferrer"&gt;public repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;p&gt;The initial concept was to propose a quick game testing someone knowledge on the carbon footprint.&lt;/p&gt;

&lt;p&gt;The Tinder-like gesture turned out to be an interesting UI solution, as it brings an appealing, and entertaining UI.&lt;/p&gt;

&lt;p&gt;After designing some key screens (Figma) and tested them through an animated After Effects prototype (After Effects), time has come to turn it into some functional code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framer-motion
&lt;/h2&gt;

&lt;p&gt;This kind of interactity perfectly fits with what Framer-Motion is capable of. We are talking about gesture event listeners and animation controlled elements.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Gesture management with Drag
&lt;/h3&gt;

&lt;p&gt;The first needed piece is the Drag gesture, that will be mapped from the swipe movement.&lt;/p&gt;

&lt;p&gt;Framer-Motion has the &lt;a href="https://www.framer.com/motion/gestures/#drag" rel="noopener noreferrer"&gt;Drag &amp;amp; Pan helpers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's a matter of turning moving elements into a "motion" element using:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;motion.div&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then apply specific Framer-Motion attributes on them.&lt;/p&gt;

&lt;p&gt;For the use-case of a swiping card, I used the following ones :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;drag (set the element draggable)&lt;/li&gt;
&lt;li&gt;dragSnapToOrigin (animate the element back to origin at release)&lt;/li&gt;
&lt;li&gt;dragElastic (set increasing friction as the element walks away from its origin)&lt;/li&gt;
&lt;li&gt;dragConstraints (maximum distance allowed from element's origin)&lt;/li&gt;
&lt;li&gt;dragTransition (apply an organic easing when the elements move away or back at release)&lt;/li&gt;
&lt;li&gt;onDrag (speak for itself)&lt;/li&gt;
&lt;li&gt;onDragStart (speak for itself)&lt;/li&gt;
&lt;li&gt;onDragEnd (speak for itself)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these events handlers greatly simplify the process to animate a card properly, so it behave with constraints and elastic transitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Value mapping with UseTransform
&lt;/h3&gt;

&lt;p&gt;The second needed piece is the transform helper to map other elements animation as we swipe left or right (e.g: the bottom action buttons scale).&lt;/p&gt;

&lt;p&gt;Framer-Motion has the &lt;a href="https://www.framer.com/motion/use-transform/" rel="noopener noreferrer"&gt;useTransform&lt;/a&gt; hook.&lt;/p&gt;

&lt;p&gt;This is the Framer-Motion helper to map any primary movements to control adjacent elements.&lt;/p&gt;

&lt;p&gt;The useTransform expose an output value based on a &lt;a href="https://www.framer.com/motion/motionvalue/" rel="noopener noreferrer"&gt;Motion value&lt;/a&gt;.&lt;br&gt;
This hook track any motion values derived from a primary element.&lt;/p&gt;

&lt;p&gt;Usually, we extract this motion value as variable from an element styled property, and pass it to the useTransform hook to map the desired output.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMotionValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;drivenX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inputX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this case, x is the Framer-Motion shorthand property for &lt;em&gt;transform: translateX()&lt;/em&gt; , and only works for element with motion tags.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Animation state tracking with useMotionValueEvent
&lt;/h3&gt;

&lt;p&gt;Lastly, a very useful thing is to trigger some actions based on some animated values.&lt;/p&gt;

&lt;p&gt;Framer-Motion has the &lt;a href="https://www.framer.com/motion/use-motion-value-event/" rel="noopener noreferrer"&gt;useMotionValueEvent&lt;/a&gt; hook.&lt;/p&gt;

&lt;p&gt;It acts like a useEffects, but is specifically designed to work with motion values.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMotionValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;useMotionValueEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;animationStart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;animation started on x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In my case, I used it to lift some updated states to a parent component, because animated adjacent components (actions buttons and colored background) are one level up:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="nf"&gt;useMotionValueEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
  &lt;span class="nf"&gt;setCardDrivenProps&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;buttonScaleBadAnswer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;drivenActionLeftScale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;buttonScaleGoodAnswer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;drivenActionRightScale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;mainBgColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;drivenBg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;})&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Swipe logic&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;In my specific case, I wanted to have a more granular control over the swiped card, so I used an additional transparent overlay as draggable element to control the card itself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Ftinder-like-card-game-with-framer-motion-elements.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Flansolo99%2Fimage%2Fupload%2Fc_fit%2Cdpr_auto%2Cq_auto%2Cw_auto%2Flansolo.dev%2Fposts%2Ftinder-like-card-game-with-framer-motion-elements.jpg" alt="Puzzle start"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is pretty much it for the Framer-Motion hooks experimented.&lt;/p&gt;

&lt;p&gt;The official doc does a pretty good job at explaining the api. For additional and more human resource, I strongly advise the &lt;a href="https://www.youtube.com/samselikoff" rel="noopener noreferrer"&gt;Sam Selikoff channel&lt;/a&gt;, a true expert with a great pedagogy on the subject.&lt;/p&gt;

&lt;h2&gt;
  
  
  Image-mask
&lt;/h2&gt;

&lt;p&gt;Another thing I struggled with was masking the card image with a custom shape, while leveraging the &lt;a href="https://nextjs.org/docs/pages/api-reference/components/image" rel="noopener noreferrer"&gt;Next.js Image&lt;/a&gt; component.&lt;/p&gt;

&lt;p&gt;I wanted to use an SVG mask but failed at a satisfaying result in term of concice syntax.&lt;/p&gt;

&lt;p&gt;I ended up using a png image using the CSS mask-image property. It works BUT, we have to put in the webkit vendor prefix to make it work.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;maskImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&lt;code&gt;url('/images/gamecard-image-mask.png')&lt;/code&gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;WebkitMaskImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&lt;code&gt;url(/images/gamecard-image-mask.png)&lt;/code&gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;maskSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;WebkitMaskSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;maskRepeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-repeat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;WebkitMaskRepeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-repeat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}}&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Contexts and RSCs&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Next.js has adopted the new React server component paradigm.&lt;/p&gt;

&lt;p&gt;This module is mostly state driven animated, so pretty much all the components are client.&lt;/p&gt;

&lt;p&gt;I could have put the text as server component with boundaries, but I didn't see the burden being worth, for this prototype.&lt;/p&gt;

&lt;p&gt;The Root Layout is the only server component and pass some initial datas to some React contexts.&lt;/p&gt;

&lt;p&gt;These contexts take care of the current game and user score.&lt;/p&gt;

&lt;p&gt;I don't want to advertise, but I learned a lot from the free &lt;a href="https://www.pronextjs.dev/tutorials" rel="noopener noreferrer"&gt;Jack Herrington course&lt;/a&gt; to refactor the way I wrote contexts and how to deal with this new paradigm, regarding management state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Framer-Motion is a huge and pretty complex library that I still explore and use on a regular basis.&lt;/p&gt;

&lt;p&gt;There is still a pile of hooks I haven't play with yet.&lt;/p&gt;

&lt;p&gt;Also don't hesitate to drop feebacks, as there is always a lot to learn from each others!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>framermotion</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>A drag and drop puzzle game with Framer-Motion</title>
      <dc:creator>Stéphane CHANGARNIER</dc:creator>
      <pubDate>Tue, 04 Jan 2022 17:16:39 +0000</pubDate>
      <link>https://dev.to/lansolo99/a-drag-and-drop-puzzle-game-with-framer-motion-4aon</link>
      <guid>https://dev.to/lansolo99/a-drag-and-drop-puzzle-game-with-framer-motion-4aon</guid>
      <description>&lt;h2&gt;
  
  
  Stack used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js + Typescript&lt;/li&gt;
&lt;li&gt;Tailwind&lt;/li&gt;
&lt;li&gt;Framer Motion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;I'm a big fan of the &lt;a href="https://www.framer.com/motion/" rel="noopener noreferrer"&gt;Framer-motion&lt;/a&gt; animation library since I started playing with it a couple of months ago.&lt;/p&gt;

&lt;p&gt;It comes in handy when I have to deal with high effect interaction designs.&lt;/p&gt;

&lt;p&gt;I decided to dig a little deep more by using it for a simple drag and drop puzzle in a side-project.&lt;/p&gt;

&lt;p&gt;I stumbled upon &lt;a href="https://dribbble.com/shots/13373054-Dogzzle" rel="noopener noreferrer"&gt;this Dribble design&lt;/a&gt; from Dwi Ash, and decided to implement it.&lt;/p&gt;

&lt;p&gt;Here is how it looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foqupsmq1tiad45nfvx5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foqupsmq1tiad45nfvx5x.png" alt="Puzzle start"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the &lt;a href="https://drag-and-drop-puzzle-with-framer-motion.vercel.app/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; and the &lt;a href="https://github.com/lansolo99/drag-and-drop-puzzle-with-framer-motion" rel="noopener noreferrer"&gt;public repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;p&gt;First, I chose a pixelated and personified version of an apple, as the main puzzle picture.&lt;/p&gt;

&lt;p&gt;This choice was made as a visual representation of the company I'm working with called &lt;a href="https://pommeclic.com" rel="noopener noreferrer"&gt;Pommeclic&lt;/a&gt; (appleclic in French).&lt;/p&gt;

&lt;p&gt;It's more complex than the original dog design, and I had to subdivide it into smaller grid cells.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhkm2og9dhtgvsh289nr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhkm2og9dhtgvsh289nr.png" alt="Subdivided apple"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I had to break the character into multiple pieces and add a grid system and pieces IDs to translate the whole thing as React components.&lt;/p&gt;

&lt;p&gt;I have done that using Figma.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F801gad2jtrij5r74c6em.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F801gad2jtrij5r74c6em.png" alt="Pieces logic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Design implementation
&lt;/h2&gt;

&lt;p&gt;I decided to go with full css implementation using a data objects to compute both empty slot zones and floating pieces around.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data objects
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/lansolo99/drag-and-drop-puzzle-with-framer-motion/blob/develop/datas/puzzleSlots.ts" rel="noopener noreferrer"&gt;Slots&lt;/a&gt; are rows of squared units, each containing utility classes that I defined using Tailwind and based on css variables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lansolo99/drag-and-drop-puzzle-with-framer-motion/blob/develop/datas/puzzlePieces.ts" rel="noopener noreferrer"&gt;Pieces&lt;/a&gt; are grouped squared units, using the same system of utility classes, this is mandatory to be able to render proper bevel effect with light and shadow bevels at the right place.&lt;/p&gt;

&lt;p&gt;Then, render the markup is only a matter of looping over these datas into the &lt;a href="https://github.com/lansolo99/drag-and-drop-puzzle-with-framer-motion/blob/develop/pages/index.tsx" rel="noopener noreferrer"&gt;JSX part&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The index page contains of course lot of additional logic that I can't explain in detail (to keep this article concise), but the whole architecture is based on this &lt;a href="https://codesandbox.io/s/5trtt" rel="noopener noreferrer"&gt;showcased CodeSandbox&lt;/a&gt; from Matt Perry.&lt;/p&gt;

&lt;p&gt;This CodeSandbox is a simple demo of a Framer Motion shared layout used within a drag and drop feature between 2 zones.&lt;/p&gt;

&lt;p&gt;My project is much more complex as I have to handle multiple start and end zones (pieces and slots).&lt;/p&gt;

&lt;h2&gt;
  
  
  Framer Motion drag
&lt;/h2&gt;

&lt;p&gt;Framer Motion has a built-in &lt;a href="https://www.framer.com/docs/examples/#drag" rel="noopener noreferrer"&gt;drag and drop feature&lt;/a&gt; that can be easily implemented on any HTML tags (set as motion.div), using specific attributes and methods (drag, onDrag, onDragStart, onDragEnd, dragElastic, dragTransition..).&lt;/p&gt;

&lt;h2&gt;
  
  
  Framer Motion shared layout
&lt;/h2&gt;

&lt;p&gt;I think Framer Motion shines when dealing with &lt;a href="https://www.framer.com/docs/animation/#shared-layout-animations" rel="noopener noreferrer"&gt;shared layouts&lt;/a&gt;. It means we can use two different components, but keep only one displayed and animate it smoothly between one and another.&lt;/p&gt;

&lt;p&gt;This what is done between a dragged piece and a positioned piece : both are defined at their final positions in their own containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other utilities
&lt;/h2&gt;

&lt;p&gt;I used multiple third party packages that I found useful along the way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/mvasin/react-div-100vh" rel="noopener noreferrer"&gt;div100vh&lt;/a&gt;: as it's a responsive and centered design, this help to deal with real viewport size on mobile&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mifi/react-lottie-player" rel="noopener noreferrer"&gt;Lottie React Player&lt;/a&gt;: to implement &lt;a href="https://lottiefiles.com/what-is-lottie" rel="noopener noreferrer"&gt;Lottie&lt;/a&gt; Json animations (the firework at puzzle completion)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lukeed/clsx" rel="noopener noreferrer"&gt;clsx&lt;/a&gt;: my go-to package to help dealing with complex conditional classes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://usehooks-ts.com/" rel="noopener noreferrer"&gt;usehooks-ts&lt;/a&gt;: A very convenient collection of utility hooks written in Typescript&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Helper
&lt;/h2&gt;

&lt;p&gt;Completing the puzzle display a reward message, and a reset button.&lt;/p&gt;

&lt;p&gt;I added a helper on the top-left corner of the screen to trigger the completion, that is useful during the development phase.&lt;/p&gt;

&lt;p&gt;Here is how the final stage looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8hl27vqgunympqesu3e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8hl27vqgunympqesu3e.png" alt="Puzzle end"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  To go deeper..
&lt;/h2&gt;

&lt;p&gt;I intend to dig more on this lib as I barely scratched the surface of what is achievable.&lt;/p&gt;

&lt;p&gt;Beside the &lt;a href="https://www.framer.com/docs/" rel="noopener noreferrer"&gt;official doc&lt;/a&gt;, I found a lot of &lt;a href="https://framerbook.com/animation/example-animations/" rel="noopener noreferrer"&gt;examples animations&lt;/a&gt; exposed in the &lt;a href="https://framerbook.com/" rel="noopener noreferrer"&gt;Framer book&lt;/a&gt; that is focused toward &lt;a href="https://www.framer.com/" rel="noopener noreferrer"&gt;Framer&lt;/a&gt;, the original prototyping tool Framer Motion is based on.&lt;/p&gt;

&lt;p&gt;I personally didn't use Framer so far but maybe should I?&lt;/p&gt;

&lt;p&gt;Don't hesitate to tell me if you identify code smells within the &lt;a href="https://github.com/lansolo99/drag-and-drop-puzzle-with-framer-motion" rel="noopener noreferrer"&gt;prototype&lt;/a&gt;, as I started using React since a bit less than a year and Typescript for a couple of months.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>framermotion</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Vue.js + Vuex form binding circuit</title>
      <dc:creator>Stéphane CHANGARNIER</dc:creator>
      <pubDate>Fri, 08 Jan 2021 06:48:20 +0000</pubDate>
      <link>https://dev.to/lansolo99/vue-js-vuex-form-binding-circuit-5oi</link>
      <guid>https://dev.to/lansolo99/vue-js-vuex-form-binding-circuit-5oi</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;The state reactivity is one of the feature that make Vue.js really stands out, especially when using the v-model attribute within a form element. We can manage the local state (i.e inside a given component) very easily.&lt;/p&gt;

&lt;p&gt;Things get a bit more complicated when someone needs to deal with Vuex store, and get the data centrally managed. We can send our data to Vuex via either actions or mutations, and can get them via mapped states or getters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem
&lt;/h3&gt;

&lt;p&gt;Vuex in its design, does not allow to change its state directly, this should be done via actions or mutations.  When using v-model on a Vuex state, we break this Vuex law and therefore get an error.&lt;/p&gt;

&lt;p&gt;To bypass this common problem, &lt;a href="https://vuex.vuejs.org/guide/forms.html" rel="noopener noreferrer"&gt;Vuex documentation&lt;/a&gt; provides a work around that make use of a modified computed property which contains getter and setter. Each form element has to be bound to this computed but I think this approach can become very verbose if our form contains a lot of fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  My approach
&lt;/h3&gt;

&lt;p&gt;After a lot of research, studying different proposed developer approaches, I’ve always used the same pattern since the beginning which I find the most robust and straightforward: &lt;strong&gt;bind the form components on a local deep copy of the Vuex state and keep updating this copy through a watcher&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mindmap
&lt;/h3&gt;

&lt;p&gt;As a picture is worth a thousands words, I made an illustrated mindmap of the full circuit to get things as clear as possible :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/lansolo99/image/upload/f_auto,q_auto/lansolo.dev/posts/vue-js-vuex-form-binding_1.png" rel="noopener noreferrer"&gt;&lt;br&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq9fp9hlllf4u0nehjf2m.jpg" alt="Alt Text"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: I’m using the fetch hook as the entry point (I always work with Nuxt), but this can be replaced with the created/mounted hook.&lt;/p&gt;

&lt;p&gt;By no mean I would pretend this is the best solution, but only one that suits me the best across all my projects so far. Let me know if you identify potential problems/optimizations with this one.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>vuex</category>
      <category>nuxt</category>
    </item>
  </channel>
</rss>
