canvas噪点悬浮动画效果
代码语言:html
所属分类:悬停
代码描述:canvas噪点悬浮动画效果
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <style> @import url("https://fonts.googleapis.com/css2?family=Red+Rose:wght@400;700&display=swap"); * { box-sizing: border-box; margin: 0; padding: 0; } body { width: 100%; height: 100vh; display: grid; place-items: center; background: #161616; } main { width: 100%; height: 100%; display: grid; place-items: center; padding: 4rem; } .cards { width: 100%; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; } .cards .card { position: relative; width: 260px; height: 360px; margin: 2rem 4rem; } .cards .card__image { position: relative; width: 100%; height: 100%; overflow: hidden; } .cards .card__image--inner { width: 100%; height: 100%; overflow: hidden; } .cards .card__image--inner canvas { transform: scale(1.02); } .cards .card__text { position: absolute; left: 0; bottom: 25%; transform: translateX(-50%); pointer-events: none; user-select: none; overflow: hidden; } .cards .card__text--inner { display: inline-block; color: #fff; font-size: 3rem; font-family: "Red Rose", Roboto; font-weight: 700; } .loading__wrapper { position: fixed; left: 0; top: 0; width: 100%; height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center; background: #000; z-index: 200; transition: opacity 500ms ease-in; } .loading__wrapper .loader__text { color: #fff; font-family: "Red Rose", Roboto; font-weight: 400; margin-bottom: 1.4rem; } .loading__wrapper.hide { pointer-events: none; user-select: none; opacity: 0; } .support { position: fixed; right: 10px; bottom: 10px; padding: 10px; display: flex; } .support a { margin: 0 10px; color: #fff; font-size: 1.8rem; backface-visibility: hidden; transition: all 150ms ease; } .support a:hover { transform: scale(1.1); } .github-corner { position: fixed; right: 0; top: 0; } .github-corner svg { color: #353; fill: #fff; clip-path: polygon(0 0, 100% 0, 100% 100%); } .github-corner:hover .octo-arm { animation: octocat-wave 0.56s; } @keyframes octocat-wave { 0%, 100% { transform: rotate(0); } 20%, 60% { transform: rotate(-20deg); } 40%, 80% { transform: rotate(10deg); } } </style> </head> <body translate="no"> <main> <div class="cards"> <div class="card"> <div class="card__image"> <div class="card__image--inner"></div> </div> <div class="card__text"> <span class="card__text--inner">DESERT</span> </div> </div> </div> </main> <div class="loading__wrapper"> <div class="loader__text"> Loading... </div> </div> <div class="support"> <a href="" target="_blank"><i class="fab fa-twitter-square"></i></a> <a href="" target="_blank"><i class="fab fa-dribbble"></i></a> </div> <script type="text/javascript" src="http://repo.bfw.wiki/bfwrepo/js/gsap.3.3.1.js"></script> <script> // this might look cluttery on codepen, you can check the repo (below) // GITHUB LINK --> https://github.com/devloop01/canvas-image-interaction // I added comments just in case you are exploring through the code. // ALL THE CARD OPTIONS LISTED BELOW --> // 1. jumpToRandomPosition: If `true` the particles on every frame will jump to random position. Else the particles will move randomly without jumping. Defaults to `false` // 2. growAndShrink: If `true` the particles will grow & shrink, it will grow .8 times larger than the radius. Defaults to `false` // 3. fill: If `true` the particles are filled with the current pixel color that they are on, else they will be stroked for that same color. Defaults to `true` // 4. bounceFromEdges: If `true` the particles will bounce back when they hit the (specified) edges, or else thay will continue their path from the opposite edges/walls. Defaults to `true`. // 5. shape: You can specify what shape of the particles. Currently you can specify any one from the following, i.e. "circle", "square", "hexagon". If not specified then it defaults to "circle" // 6. radius: You can specity the radius of the particles, defaults to "5" if not specified. // 7. randomRadius: If `true` then the particles will have random radius, else defaults to `false` // 8. maxRadius: You can specify the minimum radius of the particles, else defaults to "2" // 9. minRadius: You can specify the maximum radius of the particles, else defaults to "5" // 10. maxVelocity: You can specify the maximum velocity of the particles, else defaults to "8" // Okay that's it, that's all the options I could add. Play around and see what fits for you. // Also please STAR this project if you think it's interesting, you can even fork it and make/add something new. console.clear(); const cards = Array.from(document.querySelectorAll(".card")); const cardOptions = [{ imageURL: { default: "http://repo.bfw.wiki/bfwrepo/image/5d65eaff5a217.png?x-oss-process=image/auto-orient,1/resize,m_fill,w_400,h_600,/quality,q_90", hovered: "http://repo.bfw.wiki/bfwrepo/image/5e6831472e8f7.png" }, totalParticles: 1500, mouseRange: 80, particlesConfig: { jumpToRandomPosition: false, fill: true, randomRadius: true, minRadius: 1, maxRadius: 2 } }, ]; const imageURLS = cardOptions. map(option => Object.values(option.imageURL)). flat(); // --------------------- CLASSES ---------------------------- class App { init() { // after all images are loaded remove loader // (this is not the best way to do so but it gets the job done) loadImages(imageURLS, images => { // this array holds the images in a sub array // i.e [img, img, img, img, img, img] ==> [[img, img], [img, img], [img, img]] const splitedImagesArray = splitArray(images, 2); cards.forEach((card, index) => { new Canvas({ parent: card.querySelector(".card__image--inner"), dimensions: { width: card.getBoundingClientRect().width, height: card.getBoundingClientRect().height }, ...cardOptions[index], images: { default: splitedImagesArray[index][0], hovered: splitedImagesArray[index][1] } }); }); // hide the loading wrapper document.querySelector(".loading__wrapper").classList.add("hide"); // let the gsap animation begin gsap. timeline({ delay: 0.8, defaults: { duration: 1.5, stagger: 0.1, ease: "expo.out" } }). fromTo( cards.map(card => card.querySelector(".card__image")), { translateY: "-100%" }, { translateY: "0%" }). fromTo( cards.map(card => card.querySelector(".card__image--inner")), { translateY: "100%" }, { translateY: "0%" }, 0). fromTo( cards.map(card => card.querySelector(".card__text--inner")), { translateY: "100%" }, { duration: 1.2, translateY: "0%" }, 0.4); }); }} class Canvas { constructor(options = {}) { // the parent where the canvas will be appended this.parent = options.parent; // canvas dimensions this.dimensions = options.dimensions; // all imageURL's, images(optional) & imagesData that are required this.imageURL = options.imageURL || {}; this.images = options.images || {}; this.imagesData = options.imagesData || { default: null, hovered: null }; this.currentImageData = null; // Array where all the particles will be stored this.particles = null; this.totalParticles = options.totalParticles || 400; // boolean which changes to 'true' when hovered, oe else false this.hovered = false; // particles configs this.particlesConfig = options.particlesConfig; // mouse range and mouse particle instance this.mouseRange = options.mouseRange || null; this.mouse = null; // initialize the canvas this.init(); } init() { // create the canvas element this.canvas = document.createElement("canvas"); // get the canvas context this.ctx = this.canvas.getContext("2d"); // set the canvas dimensions this.canvas.width = this.dimensions.width; this.canvas.height = this.dimensions.height; const initialize = () => { // this variable holds the current image data this.currentImageData = this.imagesData.default; // add many Particle instances this.addParticles(this.totalParticles); // start rendering the canvas this.startRender(); // initialize all the canvas events this.initEvents(); // append the canvas on the parent this.parent.appendChild(this.canvas); }; // what happens here is if the user/dev provides the loaded image directly then use the images provided by the use directly // and if the user provides the URL for the image then load the images from the URL and initialize if ( !this.images.hasOwnProperty("default") && !this.images.hasOwnProperty("hovered")) { // load all the images that are required and after all the images are loaded the callback is called. loadImages([this.imageURL.default, this.imageURL.hovered], images => { // set the image data so that they can be accessed later when needed this.imagesData.default = returnImageData(images[0], this.dimensions); this.imagesData.hovered = returnImageData(images[1], this.dimensions); initialize(); }); } else { // set the image data so that they can be accessed later when needed this.imagesData.default = returnImageData( this.images.default, this.dimensions); this.imagesData.hovered = returnImageData( this.images.hovered, this.dimensions); initialize(); } // init mouse particle if (this.mouseRange != null) { this.mouse = new Particle({ ctx: this.ctx, position: { x: 0, y: 0 }, radius: this.mouseRange, color: "#000", avoisEdges: true, shape: "circle" }); } } initEvents() { const onMouseEnter = () => { this.hovered = true; this.currentImageData = this.imagesData.hovered; }; const onMouseLeave = () => { this.hovered = false; this.currentImageData = this.imagesData.default; }; const onMouseMove = e => { if (this.mouse != null && this.hovered) { this.mouse.position.x = e.offsetX; this.mouse.position.y = e.offsetY; } }; this.canvas.addEventListener("mouseenter", onMouseEnter); this.canvas.addEventListener("mouseleave", onMouseLeave); this.canvas.addEventListener("mousemove", onMouseMove); } addParticles(n) { this.particles = new Particles({ ctx: this.ctx, totalParticles: n, maxBounds: { width: this.dimensions.width, height: this.dimensions.height }, imageData: this.currentImageData, particlesConfig: this.particlesConfig .........完整代码请登录后点击上方下载按钮下载查看
网友评论0