zoukankan      html  css  js  c++  java
  • [XState] Nested State

    When dragging, we want to have two modes, one in 'normal' mode, another one is 'locked', we enter the locked mode by holde on 'shift' key. 

    To achieve this, we need to use nested state:

        dragging: {
          initial: "normal",
          states: {
            normal: {
              on: {
                "keydown.shift": "locked",
              },
            },
            locked: { 
              on: {
                // overwrite mousemove actions to move only
                // on x-axis
                mousemove: {
                  actions: assignOnlyX,
                },
                "keyup.shift": {
                  target: "normal",
                },
              },
            },
          },
    • When entering the dragging, we want to use 'normal' as initial state.
    • Then we defined two nested state: 'normal' and 'locked'
    • When 'shift' key is down, we go from 'normal' to 'locked', when its up, we back from 'locked' to 'normal'.
    • We also need to overwirte mousemove state's action, to only move the box in x-axis.

    Full code:

    import { createMachine, assign, interpret } from "xstate";
    
    const elBox = document.querySelector("#box");
    const elBody = document.body;
    
    const assignPoint = assign({
      px: (context, event) => event.clientX,
      py: (context, event) => event.clientY,
    });
    
    const assignPosition = assign({
      x: (context, event) => {
        return context.x + context.dx;
      },
      y: (context, event) => {
        return context.y + context.dy;
      },
      dx: 0,
      dy: 0,
      px: 0,
      py: 0,
    });
    
    const assignDelta = assign({
      dx: (context, event) => {
        return event.clientX - context.px;
      },
      dy: (context, event) => {
        return event.clientY - context.py;
      },
    });
    
    const resetPosition = assign({
      dx: 0,
      dy: 0,
      px: 0,
      py: 0,
    });
    
    const assignOnlyX = assign({
      dx: (context, event) => {
        return event.clientX - context.px;
      },
    });
    
    const dragDropMachine = createMachine({
      initial: "idle",
      context: {
        x: 0,
        y: 0,
        dx: 0,
        dy: 0,
        px: 0,
        py: 0,
      },
      states: {
        idle: {
          on: {
            mousedown: {
              actions: assignPoint,
              target: "dragging", // dragging.locked
            },
          },
        },
        dragging: {
          initial: "normal",
          states: {
            normal: {
              on: {
                "keydown.shift": "locked",
              },
            },
            locked: { 
              on: {
                // overwrite mousemove actions to move only
                // on x-axis
                mousemove: {
                  actions: assignOnlyX,
                },
                "keyup.shift": {
                  target: "normal",
                },
              },
            },
          },
          on: {
            mousemove: {
              actions: assignDelta,
              internal: false,
            },
            mouseup: {
              actions: [assignPosition],
              target: "idle",
            },
            "keyup.escape": {
              target: "idle",
              actions: resetPosition,
            },
          },
        },
      },
    });
    
    const service = interpret(dragDropMachine);
    
    service.onTransition((state) => {
      elBox.dataset.state = state.toStrings().join(" ");
    
      if (state.changed) {
        elBox.style.setProperty("--dx", state.context.dx);
        elBox.style.setProperty("--dy", state.context.dy);
        elBox.style.setProperty("--x", state.context.x);
        elBox.style.setProperty("--y", state.context.y);
      }
    });
    
    service.start();
    
    elBox.addEventListener("mousedown", (event) => {
      service.send(event);
    });
    
    elBody.addEventListener("mousemove", (event) => {
      service.send(event);
    });
    
    elBody.addEventListener("mouseup", (event) => {
      service.send(event);
    });
    
    elBody.addEventListener("keyup", (e) => {
      if (e.key === "Escape") {
        service.send("keyup.escape");
      }
    
      if (e.key === "Shift") {
        service.send("keyup.shift");
      }
    });
    
    elBody.addEventListener("keydown", (e) => {
      if (e.key === "Shift") {
        console.log("shift is down");
        service.send("keydown.shift");
      }
    });
  • 相关阅读:
    Xlua侧如何接受CSharp侧的可变参数
    C# 中如何精确地测量一段逻辑的执行时间
    C#中设计一个 ListPool 的方案
    unity中获取设备的信息
    Oracle-游标-取钱-存钱-转账
    Oracle 存储过程与java调用
    PL/SQL loop,while.for循环
    PL/SQL if case when
    PL/SQL %type %rowtype
    Oracle PLSQL入门
  • 原文地址:https://www.cnblogs.com/Answer1215/p/13410344.html
Copyright © 2011-2022 走看看