首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在React项目中添加p5.js社区库,如p5.clickable和p5.gui?

如何在React项目中添加p5.js社区库,如p5.clickable和p5.gui?
EN

Stack Overflow用户
提问于 2021-07-26 22:53:28
回答 1查看 64关注 0票数 0

目前,我正在使用以下样板创建react-p5 (https://www.npmjs.com/package/react-p5)中概述的p5草图。我

代码语言:javascript
复制
import React from "react";
import Sketch from "react-p5";

function App() {
  let setup = (p5, canvasParentRef) => {

  };
  let draw = (p5) => {
    
  };
  return (
    <div className="App">
      <Sketch setup={setup} draw={draw} className="App" />
    </div>
  );
}

export default App;

我尝试重写这些库以匹配上面的格式,然后将它们导出并导入为'import Clickable from './clickable.js'‘,但没有成功。

我该如何将这些库添加到我的React应用程序中?我仍然在通过React寻找我的方法,但这个问题一直是我的一个障碍。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-07-27 18:16:31

不幸的是,大多数p5.js附加组件都不是设计良好的JavaScript库。现代JavaScript库作为导出特定函数/类/变量的模块发布,然后您可以导入这些函数/类/变量。可以使用are ways来构造JavaScript模块,以便它们支持以不同的方式包含它们(包括经典的<script />标签导入、ES6 import语句或require()函数),但许多附加组件,如p5.clickable不能做到这一点。此外,p5.clickable还受到依赖p5.js库函数的不幸设计决策的影响(rect/fill/stroke/等)在全球范围内暴露。然而,通过一些适度的更改,我能够让它在React应用程序中工作:

p5.clickable.js的修改版本

代码语言:javascript
复制
//Determines if the mouse was pressed on the previous frame
var cl_mouseWasPressed = false;
//Last hovered button
var cl_lastHovered = null;
//Last pressed button
var cl_lastClicked = null;
//All created buttons
var cl_clickables = [];

//This function is what makes the magic happen and should be ran after
//each draw cycle.

// EDIT: Anywhere p5 was referenced, now using global.p5
global.p5.prototype.runGUI = function () {
  for (let i = 0; i < cl_clickables.length; ++i) {
    if (cl_lastHovered !== cl_clickables[i])
      cl_clickables[i].onOutside();
  }
  if (cl_lastHovered != null) {
    if (cl_lastClicked !== cl_lastHovered) {
      cl_lastHovered.onHover();
    }
  }
  if (!cl_mouseWasPressed && cl_lastClicked !== null) {
    cl_lastClicked.onPress();
  }

  // EDIT: Use this.mouseIsPressed instead of the global mouseIsPressed.
  // hopefully this works but I haven't investigated the this binding when
  // runGUI is invoked by 'post'
  if (cl_mouseWasPressed && !this.mouseIsPressed && cl_lastClicked != null) {
    if (cl_lastClicked === cl_lastHovered) {
      cl_lastClicked.onRelease();
    }
    cl_lastClicked = null;
  }
  cl_lastHovered = null;
  cl_mouseWasPressed = this.mouseIsPressed;
}

global.p5.prototype.registerMethod('post', global.p5.prototype.runGUI);

//This function is used to get the bounding size of a
//string of text for use in the 'textScaled' property
function getTextBounds(m, font, size) {
  let txt = document.createElement("span");
  document.body.appendChild(txt);

  txt.style.font = font;
  txt.style.fontSize = size + "px";
  txt.style.height = 'auto';
  txt.style.width = 'auto';
  txt.style.position = 'absolute';
  txt.style.whiteSpace = 'no-wrap';
  txt.innerHTML = m;

  let width = Math.ceil(txt.clientWidth);
  let height = Math.ceil(txt.clientHeight);
  document.body.removeChild(txt);
  return [width, height];
}

//Button Class
// EDIT: Clickable now takes a p5 instance
function Clickable(p) {
  if (!p) {
    // EDIT: If a p5 instance is not passed to the Clickable constructor,
    // fallback on global mode
    p = global;
  }

  this.x = 0;      //X position of the clickable
  this.y = 0;      //Y position of the clickable
  this.width = 100;    //Width of the clickable
  this.height = 50;    //Height of the clickable
  this.color = "#FFFFFF";    //Background color of the clickable
  this.cornerRadius = 10;    //Corner radius of the clickable
  this.strokeWeight = 2;    //Stroke width of the clickable
  this.stroke = "#000000";  //Border color of the clickable
  this.text = "Press Me";    //Text of the clickable
  this.textColor = "#000000";  //Color for the text shown
  this.textSize = 12;    //Size for the text shown
  this.textFont = "sans-serif";  //Font for the text shown
  this.textScaled = false;     //Scale the text with the size of the clickable
  
  // image options
  this.image = null; // image object from p5loadimage()
  this.tint = null; // tint image using color
  this.noTint = true; // default to disable tinting
  this.filter = null; // filter effect

  this.updateTextSize = function () {
    if (this.textScaled) {
      for (let i = this.height; i > 0; i--) {
        if (getTextBounds(this.text, this.textFont, i)[0] <= this.width && getTextBounds(this.text, this.textFont, i)[1] <= this.height) {
          console.log("textbounds: " + getTextBounds(this.text, this.font, i));
          console.log("boxsize: " + this.width + ", " + this.height);
          this.textSize = i / 2;
          break;
        }
      }
    }
  }
  this.updateTextSize();

  this.onHover = function () {
    //This function is ran when the clickable is hovered but not
    //pressed.
  }

  this.onOutside = function () {
    //This function is ran when the clickable is NOT hovered.
  }

  this.onPress = function () {
    //This function  is ran when the clickable is pressed.
  }

  this.onRelease = function () {
    //This funcion is ran when the cursor was pressed and then
    //released inside the clickable. If it was pressed inside and
    //then released outside this won't work.
  }

  this.locate = function (x, y) {
    this.x = x;
    this.y = y;
  }

  this.resize = function (w, h) {
    this.width = w;
    this.height = h;
    this.updateTextSize();
  }

  this.drawImage = function(){
    // EDIT: All references to p5 library functions now use the instance p
    p.image(this.image, this.x, this.y, this.width, this.height);
    if(this.tint && !this.noTint){
      p.tint(this.tint)
    } else {
      p.noTint();
    }
    if(this.filter){
      p.filter(this.filter);
    }
  }

  this.draw = function () {
    p.push();
    p.fill(this.color);
    p.stroke(this.stroke);
    p.strokeWeight(this.strokeWeight);
    p.rect(this.x, this.y, this.width, this.height, this.cornerRadius);
    p.fill(this.textColor);
    p.noStroke();
    if(this.image){
      this.drawImage();
    }

    p.textAlign(p.CENTER, p.CENTER);
    p.textSize(this.textSize);
    p.textFont(this.textFont);
    p.text(this.text, this.x + this.width / 2, this.y + this.height / 2);
    if (p.mouseX >= this.x && p.mouseY >= this.y &&
      p.mouseX < this.x + this.width && p.mouseY < this.y + this.height) {
      cl_lastHovered = this;
      if (p.mouseIsPressed && !cl_mouseWasPressed) {
        cl_lastClicked = this;
      }
    }
    p.pop();
  }

  cl_clickables.push(this);
}

// Export Clickable globally.
// In this case it would also be trivial to change this into a legitimate
// module by simply exporting Clickable. But I wanted to demonstrate how
// this could be done with globals
global.Clickable = Clickable;

示例用法

代码语言:javascript
复制
import React from 'react';
import Sketch from 'react-p5';

import '../lib/p5.clickable-1.2.js';
const Clickable = global.Clickable;

let x = 50;
let y = 50;

let click1;

export default (props) => {
  const setup = (p, canvasParentRef) => {
    p.createCanvas(500, 500).parent(canvasParentRef);

    // Example Clickable
    click1 = new Clickable(p);
    click1.locate(x, y);
    click1.onHover = function () {
      this.color = props.color;
      this.textColor = "#FFFFFF";
      this.text = "Yay!";
    };
    click1.onOutside = function() {
      this.color = "#FFFFFF";
      this.textColor= "#000000";
      this.text = "Press Me";
    };
  };

  const draw = (p) => {
    p.background(0);
    p.fill(props.color || 'blue');
    click1.locate(x % p.width, y);
    click1.draw();
    x++;
  };

  return <Sketch setup={setup} draw={draw} />;
};
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68532081

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档