s_
AI images, CSS And Javascript - Creatures of D-246~BETA planetexperiment

Located in a distant corner of the galaxy, planet D-246~BETA is an enigma. By day, it presents a desert landscape, but at night, it transforms into a world filled with extraordinary creatures. Explore these nocturnal wonders!

Technologies: Creating an Extraterrestrial Scene

The project employs javascript, html, and css to bring this extraterrestrial world to life. By combining graphics generated through artificial intelligence with digital retouching techniques, the characters and environment of D-246~BETA have been crafted. Interactive elements respond to mouse movements, making the user experience more dynamic and fun.

S_turn: A Javascript Tool for Visualization

At the heart of this project is 'S_turn' a Javascript tool for controlling visual element rotation using mouse position. Available on GitHub for anyone wanting to experiment or improve it, 'S_turn' goes beyond simple animations, opening up a range of possibilities for web design professionals.

A Fun and Innovative Experiment

This project is more than just an exploration of web technologies; it's also an experiment in enjoyment and testing the capabilities of 'S_turn'. Developing this Javascript object and applying it in an interactive context has been a creative way to showcase what can be achieved with a mix of imagination and technical skills in web programming.

S_turn object

const s_isMobile = () => window.matchMedia("only screen and (max-width: 760px)").matches;
class S_turn {
  selector;
  degreeRange;
  logImportant = false;
  yOffset = 0;
  yDeviation = 0;
  xOffset = 0;
  xDeviation = 0;
  followDeacceleration = 1;
  gDeviation = 0;
  gMulFactor = 1;
  excludeWithS_fex = [];
  followOnHoveringElement = ".night";
  easyReturn = {
    on: false,
    includeRotation: false,
    includeOrigin: true,
    transitionTime: 0,
    tFunction: "linear",
  };
  #areElementsExluded = false;
  isCursorInactive;
  isCursorOut = false;
  runningControler = true;
  eInside = true;
  constructor(selector, options = null) {
    if (selector.length < 1) {
      throw new Error("Selector is mandatory!");
    }
    this.selector = `[S_f*="${selector}"]`;
    //////////////////////////////////////////////////////////////OPTIONS
    this.#optionsAsignation(options);
    this.rotateStart = (ev) => this.#followStart(ev);
    this.eventListenerFunction = this.rotateStart.bind(this);
    this["abortController"] = new AbortController();
    if (this.detectWindowLeaveEnter) {
    }
    if (!s_isMobile()) {
      document
        .querySelector(this.followOnHoveringElement)
        .addEventListener("pointermove", this.eventListenerFunction, { signal: this["abortController"].signal });
    } else {
      document
        .querySelector(this.followOnHoveringElement)
        .addEventListener("touchmove", this.eventListenerFunction, { passive: false, signal: this["abortController"].signal });
    }
    this.resize = () => this.#resize();
    this.resizeFunction = this.resize.bind(this);
    window.addEventListener("resize", this.resizeFunction);
    window.addEventListener("scroll", this.resizeFunction);
    // console.log(this);
  }
  #resize() {
    document.querySelectorAll(this.selector).forEach((e, i) => {
      this.#position(e, i);
    });
  }
  #optionsAsignation = (options) => {
    if (options) {
      options.degreeRange && (this.degreeRange = options.degreeRange);
      options.xOffset && (this.xOffset = options.xOffset);
      options.yOffset && (this.yOffset = options.yOffset);
      options.yDeviation && (this.yDeviation = options.yDeviation);
      options.xDeviation && (this.xDeviation = options.xDeviation);
      options.gDeviation && (this.gDeviation = options.gDeviation);
      options.gMulFactor && (this.gMulFactor = options.gMulFactor);
      options.followOnHoveringElement && (this.followOnHoveringElement = options.followOnHoveringElement);
      options.easyReturn && (this.easyReturn = options.easyReturn);
      options.followDeacceleration && (this.followDeacceleration = options.followDeacceleration);
      if (options.excludeWithS_fex) {
        this.excludeWithS_fex = options.excludeWithS_fex;
        this.#areElementsExluded = true;
      }
    }
    let element = document.querySelectorAll(this.selector);
    element.forEach((e, i) => {
      if (i !== undefined) {
        this["CenterX_var_Comp" + i] = 0;
        this["CenterY_var_Comp" + i] = 0;
      }
      if (options) {
        if (options.xOffsetCompensation) this["CenterX_var_Comp" + i] = options.xOffsetCompensation;
        if (options.yOffsetCompensation) this["CenterY_var_Comp" + i] = options.yOffsetCompensation;
      }
      this.#position(e, i);
    });
  };
  #position = (e, i) => {
    let xOffset = this.xOffset === 0 ? "50%" : e.clientWidth / 2 + this.xOffset;
    let xUnit = this.xOffset === 0 ? "" : "px";
    let yOffset = this.yOffset === 0 ? "50%" : e.clientHeight / 2 + this.yOffset;
    let yUnit = this.yOffset === 0 ? "" : "px";
    let trOr = xOffset + xUnit + " " + yOffset + yUnit;
    e.style.transformOrigin = trOr;
    if (this.yOffset != 0) e.style.top = this.yOffset * -1 + "px";
    if (this.xOffset != 0) e.style.left = this.xOffset * -1 + "px";
    this["CenterY_var" + i] = Math.round(e.getBoundingClientRect().top + e.clientHeight / 2);
    this["CenterX_var" + i] = Math.round(e.getBoundingClientRect().left + e.clientWidth / 2);
  };
  #followStart(event) {
    event.preventDefault();
    const coEvt = s_isMobile() ? [event] : event.getCoalescedEvents();
    const elements = document.querySelectorAll(this.selector);
    let hovering_excluded;
    if (s_isMobile()) {
      let targetElement = document.elementFromPoint(event.targetTouches[0].clientX, event.targetTouches[0].clientY);
      if (targetElement && targetElement.attributes.s_fex?.value) {
        hovering_excluded = targetElement.attributes.s_fex?.value;
      }
    } else {
      hovering_excluded = event.target.attributes.s_fex?.value;
    }
    this.isCursorInactive = this.excludeWithS_fex?.some((e) => e === hovering_excluded) ? true : false;
    const followState = !this.isCursorInactive || !this.#areElementsExluded ? "active" : "inactive"; // o no hi ha excludes o esà fora dels excludes
    if (this.runningControler) {
      elements.forEach((e, i) => {
        this["doItOnce" + i] === "undefined" && (this["doItOnce" + i] = false);
        if (followState === "active" && this.eInside) {
          if (!this["event_count" + i]) {
            this["event_count" + i] = 0;
          }
          for (let ev of coEvt) {
            // console.log(ev.touches[0].clientX);
            this["event_count" + i]++;
            if (this["event_count" + i] % this.followDeacceleration === 0) {
              let ySum = this.yOffset === 0 ? 0 : this.yOffset > 0 ? 180 : 0;
              let xSum = this.xOffset === 0 ? 0 : this.xOffset > 0 ? 270 : 90;
              let rad = Math.atan2(
                (s_isMobile() ? ev.targetTouches[0].clientX : ev.clientX) - (this["CenterX_var" + i] - this["CenterX_var_Comp" + i]),
                (s_isMobile() ? ev.targetTouches[0].clientY : ev.clientY) - (this["CenterY_var" + i] - this["CenterY_var_Comp" + i])
              );
              let rotation =
                (rad * (180 / Math.PI) * -1 + (this.yOffset === 0 ? xSum + this.xDeviation : ySum + this.yDeviation) + this.gDeviation) * this.gMulFactor;
              if (this.degreeRange && rotation > this.degreeRange[0] && rotation < this.degreeRange[1]) {
                e.style.transform = "rotate(" + rotation + "deg)";
              } else if (!this.degreeRange) {
                e.style.transform = "rotate(" + rotation + "deg)";
              }
            }
          }
        }
        if (this.easyReturn) {
          if (!this.isCursorInactive) {
            while (this["doItOnce" + i]) {
              this["doItOnce" + i] = false;
              this.#position(e);
              this["retWhenHoverTimeout" + i] = setTimeout(() => {
                this.#resume(e);
              }, this.easyReturn.transitionTime * 1000 + 100);
            }
          } else {
            while (!this["doItOnce" + i]) {
              this["doItOnce" + i] = true;
              clearTimeout(this["retWhenHoverTimeout" + i]);
              this.#pause(e);
            }
          }
        }
      });
    }
  }
  pause(ret = false) {
    if (ret) {
      const element = document.querySelectorAll(this.selector);
      element.forEach((e, i) => {
        clearTimeout(this["retPauseResumeTimeout" + i]);
        this.#pause(e);
      });
    }
    this.runningControler = false; //eventRunning
  }
  #pause(e) {
    e.style.transitionTimingFunction = this.easyReturn.tFunction;
    e.style.transitionProperty = "transform,transform-origin,top,left";
    e.style.transitionDuration = this.easyReturn.transitionTime + "s";
    if (this.easyReturn.includeRotation) e.style.transform = "rotate(0deg)"; // si inclou el rotate
    if (this.easyReturn.includeOrigin) {
      e.style.transformOrigin = "50% 50%";
      e.style.top = "0";
      e.style.left = "0";
    }
  }
  resume(ret = false) {
    const element = document.querySelectorAll(this.selector);
    element.forEach((e, i) => {
      if (ret) {
        this.runningControler = true; //eventRunning
        this.#position(e);
        this["retPauseResumeTimeout" + i] = setTimeout(() => {
          this.#resume(e);
        }, this.easyReturn.transitionTime * 1000 + 100);
      } else {
        this.#resume(e);
        this.#position(e);
        this.runningControler = true; //eventRunning
      }
    });
  }
  #resume(e) {
    e.style.transitionProperty = null;
    e.style.transitionDuration = null;
    e.style.transitionTimingFunction = null;
  }
  stop() {
    this["abortController"].abort();
  }
  update(options) {
    this.#optionsAsignation(options);
  }
}


//////////////////////////////////////////////////////USAGE
let eyes = new S_turn("u1", {
  followOnHoveringElement: "body",
  // degreeRange:[180,360],
  yOffset: 0,
  yOffsetCompensation: 0,
  yDeviation: 0,
  xOffset: 10,
  xOffsetCompensation: 0,
  xDeviation: 0,
  followDeacceleration: 0,
  gDeviation: 0,
  gMulFactor: 0,
  excludeWithS_fex: ["sign"],
  easyReturn: {
    on: true,
    includeRotation: false,
    includeOrigin: true,
    transitionTime: 0.2,
    tFunction: "cubic-bezier(1, 0, 0, 1.5)",
  },
});

Technology Stack Used

This project features: Exploration of day and night on planet D-246~BETAInteractive extraterrestrial scene creation with JavaScriptHTMLand CSSAI-generated graphics combined with digital retouchingUser-responsive interactive web elements'S_turn' - a Javascript tool for visual rotation controlAvailable on GitHub for public enhancementFun and innovative web technology experimentCreative showcase of web programming skills