SOURCE

console 命令行工具 X clear

                    
>
console
<div class="frame">
   <div class="corner_topleft"></div>
   <div class="corner_topright"></div>
   <div class="corner_bottomleft"></div>
   <div class="corner_bottomright"></div>
   
   <div class="camera">
      <div class="map pixel-art">
         <div class="character" facing="down" walking="true">
            <div class="shadow pixel-art"></div>
            <div class="character_spritesheet pixel-art"></div>
         </div>
      </div>

      <div class="dpad">
         <div class="DemoDirectionUI flex-center">
               <button class="dpad-button dpad-left">
                   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
                       <path class="Arrow_outline-top"  stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
                       <path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h5M7 3h5M1 4h4M7 4h5M1 5h3M7 5h5M1 6h4M7 6h5M1 7h5M7 7h5M1 8h11" />
                       <path class="Arrow_arrow-inset"  stroke="#434343" d="M6 3h1M5 4h1M4 5h1" />
                       <path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 4h1M5 5h2M5 6h2M6 7h1" />
                       <path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
                       <path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
                       <path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
                   </svg>
               </button>
               <button class="dpad-button dpad-up">
                   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
                       <path class="Arrow_outline-top"  stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
                       <path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h11M1 4h5M7 4h5M1 5h4M8 5h4M1 6h3M9 6h3M1 7h11M1 8h11" />
                       <path class="Arrow_arrow-inset"  stroke="#434343" d="M6 4h1M5 5h1M7 5h1" />
                       <path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 5h1M4 6h5" />
                       <path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
                       <path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
                       <path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
                   </svg>
               </button>
               <button class="dpad-button dpad-down">
                   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
                       <path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
                       <path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h11M1 4h3M9 4h3M1 5h4M8 5h4M1 6h5M7 6h5M1 7h11M1 8h11" />
                       <path class="Arrow_arrow-inset" stroke="#434343" d="M4 4h5" />
                       <path class="Arrow_arrow-body" stroke="#5f5f5f" d="M5 5h3M6 6h1" />
                       <path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
                       <path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
                       <path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
                   </svg>
               </button>
               <button class="dpad-button dpad-right">
                   <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
                       <path class="Arrow_outline-top"  stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
                       <path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h5M7 3h5M1 4h5M8 4h4M1 5h5M9 5h3M1 6h5M8 6h4M1 7h5M7 7h5M1 8h11" />
                       <path class="Arrow_arrow-inset"  stroke="#434343" d="M6 3h1M7 4h1M8 5h1" />
                       <path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 4h1M6 5h2M6 6h2M6 7h1" />
                       <path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
                       <path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
                       <path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
                   </svg>
               </button>
           </div>
      </div>
   </div>
</div>

<style>
:root {
   --pixel-size: 2px;
   --grid-cell: calc( var(--pixel-size) * 16);
   --bg: #9fa7e4;
}
@media( min-width: 700px ) {
   :root {
      --pixel-size: 3px;
   }
}
@media( min-width: 1000px ) {
   :root {
      --pixel-size: 4px;
   }
}

html, body {
   height: 100%;
}

body {
   background: var(--bg);
   display: flex;
   align-items: center;
   justify-content: center;
}

.pixel-art {
   image-rendering: pixelated;
}

.frame {
   width: calc(var(--pixel-size) * 160);
   height: calc(var(--pixel-size) * 144);
   outline: var(--pixel-size) solid #fff;
   z-index:1; 
   position:relative;
}

.camera {
   width: calc(var(--pixel-size) * 160);
   height: calc(var(--pixel-size) * 144);
   overflow: hidden;
   background: #61ddf7;
   position:relative;
}

.map {
   image-rendering: pixelated;
   background-image: url("https://assets.codepen.io/21542/CameraDemoMap.png");
   background-size: 100%;
   width: calc(13 * var(--grid-cell));
   height: calc(10 * var(--grid-cell));
   position: relative;
}

.character {
   width: calc( var(--grid-cell)* 2 );
   height: calc( var(--grid-cell)* 2 );
   position: absolute;
   overflow:hidden;
}

.shadow {
   width: calc( var(--grid-cell)* 2 );
   height: calc( var(--grid-cell)* 2 );
   position: absolute;
   left:0;
   top:0;
   background: url("https://assets.codepen.io/21542/DemoRpgCharacterShadow.png") no-repeat no-repeat;
   background-size: 100%;
}

.character_spritesheet {
   position: absolute;
   background: url("https://assets.codepen.io/21542/DemoRpgCharacter.png") no-repeat no-repeat;
   background-size: 100%;
   width: calc( var(--grid-cell)* 8 );
   height: calc( var(--grid-cell)* 8 );
}

.character[facing="right"] .character_spritesheet {
   background-position-y: calc( var(--pixel-size) * -32 );
}
.character[facing="up"] .character_spritesheet {
   background-position-y: calc( var(--pixel-size) * -64 );
}
.character[facing="left"] .character_spritesheet {
   background-position-y: calc( var(--pixel-size) * -96 );
}
.character[walking="true"] .character_spritesheet {
   animation: walkAnimation 0.6s steps(4) infinite; 
}

@keyframes walkAnimation {
  from {
    transform: translate3d(0%,0%,0);
  }
  to {
    transform: translate3d(-100%,0%,0);
  }
}

.dpad {
   position:absolute;
   right: calc(var(--pixel-size) * 2);
   bottom: calc(var(--pixel-size) * 2);
   width: calc(var(--pixel-size) * 37);
   height: calc(var(--pixel-size) * 38);
}
.dpad-button {
   appearance:none;
   outline:0;
   border:0;
   background:transparent;
   padding:0;
   cursor:pointer;
}
.dpad-button svg {
   display:block;
   height: calc(var(--pixel-size) * 13);
}

.dpad-button.pressed .Arrow_arrow-inset { stroke:#07c2cc; }
.dpad-button.pressed .Arrow_arrow-body { stroke:#17dfea; }

.dpad-up {
   position: absolute;
   left: calc(var(--pixel-size) * 12);
   top:0;
}
.dpad-down {
   position: absolute;
   bottom:var(--pixel-size);
   left: calc(var(--pixel-size) * 12);
}
.dpad-left {
   position: absolute;
   top: calc(var(--pixel-size) * 12);
   left:0;
}
.dpad-right {
   position: absolute;
   top: calc(var(--pixel-size) * 12);
   right:0;
}

.dpad {
   user-select:none;
}

.corner_topleft,
.corner_topright,
.corner_bottomleft,
.corner_bottomright {
   position: absolute;
   width: var(--pixel-size);
   height: var(--pixel-size);
   background: var(--bg);
   z-index:2;
}

.corner_topleft {
   top: calc(var(--pixel-size) * -1);
   left: calc(var(--pixel-size) * -1);
}
.corner_topright {
   top: calc(var(--pixel-size) * -1);
   right: calc(var(--pixel-size) * -1);
}
.corner_bottomleft {
   bottom: calc(var(--pixel-size) * -1);
   left: calc(var(--pixel-size) * -1);
}
.corner_bottomright {
   bottom: calc(var(--pixel-size) * -1);
   right: calc(var(--pixel-size) * -1);
}
</style>

<script>
var character = document.querySelector(".character");
var map = document.querySelector(".map");

//start in the middle of the map
var x = 90;
var y = 34;
var held_directions = []; //State of which arrow keys we are holding down
var speed = 1; //How fast the character moves in pixels per frame

const placeCharacter = () => {
   
   var pixelSize = parseInt(
      getComputedStyle(document.documentElement).getPropertyValue('--pixel-size')
   );
   
   const held_direction = held_directions[0];
   if (held_direction) {
      if (held_direction === directions.right) {x += speed;}
      if (held_direction === directions.left) {x -= speed;}
      if (held_direction === directions.down) {y += speed;}
      if (held_direction === directions.up) {y -= speed;}
      character.setAttribute("facing", held_direction);
   }
   character.setAttribute("walking", held_direction ? "true" : "false");
   
   //Limits (gives the illusion of walls)
   var leftLimit = -8;
   var rightLimit = (16 * 11)+8;
   var topLimit = -8 + 32;
   var bottomLimit = (16 * 7);
   if (x < leftLimit) { x = leftLimit; }
   if (x > rightLimit) { x = rightLimit; }
   if (y < topLimit) { y = topLimit; }
   if (y > bottomLimit) { y = bottomLimit; }
   
   var camera_left = pixelSize * 66;
   var camera_top = pixelSize * 42;
   
   map.style.transform = `translate3d( ${-x*pixelSize+camera_left}px, ${-y*pixelSize+camera_top}px, 0 )`;
   character.style.transform = `translate3d( ${x*pixelSize}px, ${y*pixelSize}px, 0 )`;  
}

//Set up the game loop
const step = () => {
   placeCharacter();
   window.requestAnimationFrame(() => {
      step();
   })
}
step(); //kick off the first step!

/* Direction key state */
const directions = {
   up: "up",
   down: "down",
   left: "left",
   right: "right",
}
const keys = {
   38: directions.up,
   37: directions.left,
   39: directions.right,
   40: directions.down,
}
document.addEventListener("keydown", (e) => {
   var dir = keys[e.which];
   if (dir && held_directions.indexOf(dir) === -1) {
      held_directions.unshift(dir)
   }
})

document.addEventListener("keyup", (e) => {
   var dir = keys[e.which];
   var index = held_directions.indexOf(dir);
   if (index > -1) {
      held_directions.splice(index, 1)
   }
});

/* Dpad functionality for mouse and touch */
var isPressed = false;
const removePressedAll = () => {
   document.querySelectorAll(".dpad-button").forEach(d => {
      d.classList.remove("pressed")
   })
}
document.body.addEventListener("mousedown", () => {
   isPressed = true;
})
document.body.addEventListener("mouseup", () => {
   isPressed = false;
   held_directions = [];
   removePressedAll();
})
const handleDpadPress = (direction, click) => {   
   if (click) {
      isPressed = true;
   }
   held_directions = (isPressed) ? [direction] : []
   
   if (isPressed) {
      removePressedAll();
      document.querySelector(".dpad-"+direction).classList.add("pressed");
   }
}

// Bind events for the dpad
document.querySelector(".dpad-left").addEventListener("touchstart", (e) => handleDpadPress(directions.left, true));
document.querySelector(".dpad-up").addEventListener("touchstart", (e) => handleDpadPress(directions.up, true));
document.querySelector(".dpad-right").addEventListener("touchstart", (e) => handleDpadPress(directions.right, true));
document.querySelector(".dpad-down").addEventListener("touchstart", (e) => handleDpadPress(directions.down, true));

document.querySelector(".dpad-left").addEventListener("mousedown", (e) => handleDpadPress(directions.left, true));
document.querySelector(".dpad-up").addEventListener("mousedown", (e) => handleDpadPress(directions.up, true));
document.querySelector(".dpad-right").addEventListener("mousedown", (e) => handleDpadPress(directions.right, true));
document.querySelector(".dpad-down").addEventListener("mousedown", (e) => handleDpadPress(directions.down, true));

document.querySelector(".dpad-left").addEventListener("mouseover", (e) => handleDpadPress(directions.left));
document.querySelector(".dpad-up").addEventListener("mouseover", (e) => handleDpadPress(directions.up));
document.querySelector(".dpad-right").addEventListener("mouseover", (e) => handleDpadPress(directions.right));
document.querySelector(".dpad-down").addEventListener("mouseover", (e) => handleDpadPress(directions.down));

// Add touchend events for better mobile support
document.querySelector(".dpad-left").addEventListener("touchend", () => { held_directions = []; removePressedAll(); });
document.querySelector(".dpad-up").addEventListener("touchend", () => { held_directions = []; removePressedAll(); });
document.querySelector(".dpad-right").addEventListener("touchend", () => { held_directions = []; removePressedAll(); });
document.querySelector(".dpad-down").addEventListener("touchend", () => { held_directions = []; removePressedAll(); });
</script>