Floating Island | Html Css And JavaScript

Follow Us On Telegram  

Get Full Source Code Zipped File  

Telegram link

HTML
<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>CodeAtNow - Floating island</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<link rel="stylesheet" href="./style.css">

</head>
<body>
<!-- partial:index.partial.html -->
<div class="container">
  <canvas></canvas>
</div>
<!-- partial -->
  <script src='https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.4/gsap.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/r118/three.min.js'></script>
<script src='https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js'></script><script  src="./script.js"></script>

</body>
</html>
CSS
html, body {
  height: 100%;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
}

svg {
  width: 100%;
}

.container {
  width: 100%;
  height: 100%;
}
.container canvas {
  display: block;
  width: 100%;
  height: 100%;
}
JS
console.clear();
let scene, camera, renderer, controls;
let canvas = document.querySelector("canvas");
let width = window.innerWidth;
let height = window.innerHeight;
let fov = 75;
let aspectRatio = width / height;
let near = 0.001;
let far = 100;
let mainGroup = new THREE.Group();
let colors = {
  blue: 0x71b6f7,
  brown: 0x744436,
  brown2: 0xC88247,
  red: 0xfd4d50,
  green: 0xa4d740,
  green2: 0x66b888,
  green3: 0x2C9D3E,
  house: 0xfce3ad,
  purple: 0x6e5370,
  gold: 0xFFF09C,
  grey: 0xB7B398,
  greyBrown: 0xB7B398 };


const setup = () => {
  // scene
  scene = new THREE.Scene();
  scene.fog = new THREE.FogExp2(0x9ac2fe, 0.14);
  // camera
  camera = new THREE.PerspectiveCamera(fov, aspectRatio, near, far);
  scene.add(camera);
  // renderer
  renderer = new THREE.WebGLRenderer({
    antialias: true,
    canvas: canvas });

  renderer.setSize(width, height);
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setClearColor(0x9ac2fe);
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  renderer.shadowMapSoft = true;
  // controls
  controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.autoRotate = true;
  controls.autoRotateSpeed = 1.6;
  camera.position.set(3.56, 2, 3.4);
  // lights 
  let light = new THREE.HemisphereLight(0xffffff, 0x5c9cfe, 1.1);
  scene.add(light);
  //
  let spot = new THREE.SpotLight(0xF6FAFD, 0.06);
  spot.castShadow = true;
  spot.shadow.camera.left = -1;
  spot.shadow.camera.right = -1;
  spot.shadow.camera.top = -1;
  spot.shadow.camera.bottom = -1;
  spot.shadow.camera.near = 1;
  spot.shadow.camera.far = 100;
  spot.shadow.mapSize.width = 2048;
  spot.shadow.mapSize.height = 2048;
  spot.shadow.camera.fov = 100;
  spot.position.set(-3, 3, 0);
  camera.add(spot);
  // group for all other groups
  mainGroup = new THREE.Group();
  scene.add(mainGroup);
  camera.lookAt(0, 0, 0);
};

// objects
let islandGroup = new THREE.Group();
const createIsland = () => {
  // earth
  let coneGeo = new THREE.ConeBufferGeometry(2, 3, 8);
  let mat = new THREE.MeshLambertMaterial({
    color: colors.brown });

  let cone = new THREE.Mesh(coneGeo, mat);
  cone.rotation.x = THREE.Math.degToRad(180);
  cone.position.set(0, -1.75, 0);
  islandGroup.add(cone);
  // grass
  let boxGeo = new THREE.BoxBufferGeometry();
  let mat2 = new THREE.MeshLambertMaterial({
    color: colors.green });

  let grass = new THREE.Mesh(boxGeo, mat2);
  grass.scale.set(4, 0.5, 4);
  grass.receiveShadow = true;
  islandGroup.add(grass);
  // water
  let mat3 = new THREE.MeshLambertMaterial({
    color: colors.blue });

  let water = new THREE.Mesh(boxGeo, mat3);
  water.receiveShadow = true;
  islandGroup.add(water);
  water.scale.set(0.5, 0.5, 4);
  water.position.set(0.75, 0.005, 0.005);
  mainGroup.add(islandGroup);
};

// waterfall particles
let dropCount = 100;
let drops = [];
// water details
let detailCount = 24;
let dets = [];
let detailCount2 = 8;
let dets2 = [];

// creating particles function
let particleGeo, particleMat, particle;
const createParticles = (color, particleAmount, particleArray, scaleX, scaleY, scaleZ, posX, posX2, posY, posY2, posZ, posZ2, opacity, rotX, rotY, rotZ) => {
  particleGeo = new THREE.BoxBufferGeometry();
  particleMat = new THREE.MeshLambertMaterial({
    color: color,
    transparent: true });

  for (let i = 0; i < particleAmount; i++) {
    particle = new THREE.Mesh(particleGeo, particleMat);
    islandGroup.add(particle);
    particleArray.push(particle);
    particle.scale.set(scaleX, scaleY, scaleZ);
    particle.position.set(THREE.Math.randFloat(posX, posX2), THREE.Math.randFloat(posY, posY2), THREE.Math.randFloat(posZ, posZ2));
    particle.material.opacity = opacity;
    particle.rotation.set(rotX, THREE.Math.degToRad(rotY), rotZ);
  }
};

// function to create various box like shapes of the house and the mailbox
const createBoxShape = (x, y, z, xPos, yPos, zPos, color, rShadow, cShadow, group) => {
  let geo = new THREE.BoxBufferGeometry(x, y, z);
  let mat = new THREE.MeshLambertMaterial({ color: color });
  let mesh = new THREE.Mesh(geo, mat);
  mesh.position.set(xPos, yPos, zPos);
  mesh.receiveShadow = rShadow;
  mesh.castShadow = cShadow;
  group.add(mesh);
};

let treeGroup = new THREE.Group();
const createTree = (trunkX, trunkY, trunkZ, leavesX, leavesY, leavesZ) => {
  // trunk
  let geo = new THREE.CylinderBufferGeometry(0.1, 0.1, 1, 10);
  let mat = new THREE.MeshLambertMaterial({
    color: colors.brown });

  let trunk = new THREE.Mesh(geo, mat);
  treeGroup.add(trunk);
  trunk.position.set(trunkX, trunkY, trunkZ);
  trunk.castShadow = true;
  trunk.receiveShadow = true;
  // leaves
  let geo2 = new THREE.SphereBufferGeometry(0.25, 12, 12);
  let mat2 = new THREE.MeshLambertMaterial({
    color: colors.green2 });

  let treeLeaves = new THREE.Mesh(geo2, mat2);
  treeLeaves.position.set(leavesX, leavesY, leavesZ);
  treeLeaves.castShadow = true;
  treeLeaves.receiveShadow = true;
  treeGroup.add(treeLeaves);
  mainGroup.add(treeGroup);
};

const createBush = (x, y, z) => {
  let geo = new THREE.SphereBufferGeometry(0.15, 8, 8);
  let mat = new THREE.MeshLambertMaterial({ color: colors.green3 });
  let bush = new THREE.Mesh(geo, mat);
  bush.position.set(x, y, z);
  bush.receiveShadow = true;
  treeGroup.add(bush);
};

// house
let houseGroup = new THREE.Group();

// roof window function
const createRoofWindow = (x, y, z, color, radB, radT, height, segments) => {
  let geo = new THREE.CylinderBufferGeometry(radB, radT, height, segments);
  let mat = new THREE.MeshLambertMaterial({ color: color });
  let window = new THREE.Mesh(geo, mat);
  window.rotation.z = THREE.Math.degToRad(90);
  window.position.set(x, y, z);
  houseGroup.add(window);
};

const createHouse = () => {
  let boxGeo = new THREE.BoxBufferGeometry();
  // house
  let houseMat = new THREE.MeshLambertMaterial({ color: colors.house });
  let house = new THREE.Mesh(boxGeo, houseMat);
  house.position.set(-1, 0.75, 1);
  house.scale.set(1.2, 1, 1.5);
  house.receiveShadow = true;
  house.castShadow = true;
  houseGroup.add(house);
  // roof
  let roofGeo = new THREE.ConeBufferGeometry(1.1, 0.7, 4);
  let roofMat = new THREE.MeshLambertMaterial({ color: colors.red });
  let roof = new THREE.Mesh(roofGeo, roofMat);
  roof.position.set(-1, 1.6, 1);
  roof.rotation.y = THREE.Math.degToRad(45);
  roof.castShadow = true;
  roof.receiveShadow = true;
  houseGroup.add(roof);
  // roof chimney
  let chimneyMat = new THREE.MeshLambertMaterial({ color: colors.house });
  let chimney = new THREE.Mesh(boxGeo, chimneyMat);
  chimney.position.set(-1, 1.6, 0.6);
  chimney.scale.set(0.2, 0.3, 0.2);
  chimney.receiveShadow = true;
  houseGroup.add(chimney);
  // door
  let doorMat = new THREE.MeshLambertMaterial({ color: colors.purple });
  let door = new THREE.Mesh(boxGeo, doorMat);
  door.scale.set(0.2, 0.5, 0.35);
  door.position.set(-0.49, 0.545, 1);
  houseGroup.add(door);
  // doorknob
  let knobG = new THREE.SphereBufferGeometry(0.025, 8, 8);
  let knobMat = new THREE.MeshLambertMaterial({ color: colors.gold });
  let knob = new THREE.Mesh(knobG, knobMat);
  houseGroup.add(knob);
  knob.position.set(-0.39, 0.5, 1.14);
  // doorstep 
  createBoxShape(0.05, 0.05, 0.35, -0.38, 0.27, 1, colors.grey, false, false, houseGroup);
  createBoxShape(0.05, 0.05, 0.35, -0.34, 0.25, 1, colors.grey, false, false, houseGroup);
  // windows 
  createRoofWindow(-0.65, 1.6, 1, colors.blue, 0.1, 0.1, 0.2, 12);
  createRoofWindow(-0.67, 1.60, 1, colors.red, 0.12, 0.12, 0.22, 12);
  // roof window bars
  createBoxShape(0.02, 0.24, 0.025, -0.55, 1.6, 1, colors.red, false, false, houseGroup);
  createBoxShape(0.02, 0.02, 0.2, -0.55, 1.62, 1, colors.red, false, false, houseGroup);
  // left window
  createBoxShape(0.05, 0.3, 0.3, -0.41, 0.65, 1.45, colors.blue, false, false, houseGroup);
  // vertical bars
  createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 1.45, colors.brown2, false, false, houseGroup);
  createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 1.60, colors.brown2, false, false, houseGroup);
  createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 1.30, colors.brown2, false, false, houseGroup);
  // horizontal bars
  createBoxShape(0.05, 0.025, 0.325, -0.40, 0.8, 1.45, colors.brown2, false, false, houseGroup);
  createBoxShape(0.05, 0.025, 0.325, -0.40, 0.65, 1.45, colors.brown2, false, false, houseGroup);
  createBoxShape(0.05, 0.025, 0.325, -0.40, 0.5, 1.45, colors.brown2, false, false, houseGroup);
  // right window
  createBoxShape(0.05, 0.3, 0.3, -0.41, 0.65, 0.55, colors.blue, false, false, houseGroup);
  // vertical bars
  createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 0.55, colors.brown2, false, false, houseGroup);
  createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 0.70, colors.brown2, false, false, houseGroup);
  createBoxShape(0.05, 0.3, 0.025, -0.40, 0.65, 0.40, colors.brown2, false, false, houseGroup);
  // horizontal bars
  createBoxShape(0.05, 0.025, 0.325, -0.40, 0.8, 0.55, colors.brown2, false, false, houseGroup);
  createBoxShape(0.05, 0.025, 0.325, -0.40, 0.65, 0.55, colors.brown2, false, false, houseGroup);
  createBoxShape(0.05, 0.025, 0.325, -0.40, 0.5, 0.55, colors.brown2, false, false, houseGroup);
  mainGroup.add(houseGroup);
};

// chimney smoke
const puffCount = 2;
let puffs = [];
let puff;
const createPuffs = () => {
  for (let i = 0; i < puffCount; i++) {
    const geo = new THREE.SphereBufferGeometry(0.1, 6, 6);
    const mat = new THREE.MeshLambertMaterial({ color: 0xffffff, transparent: true });
    puff = new THREE.Mesh(geo, mat);
    puffs.push(puff);
    houseGroup.add(puff);
    puff.position.set(-1, 1.74, 0.6);
    puff.scale.set(0, 0, 0);
  }
};

// mailbox
let mailBoxGroup = new THREE.Group();

const createMailBoxRoof = () => {
  let mailRoofG = new THREE.ConeBufferGeometry(0.16, 0.1, 4);
  let mailRoofMat = new THREE.MeshLambertMaterial({ color: colors.brown2 });
  let mailRoofMesh = new THREE.Mesh(mailRoofG, mailRoofMat);
  mailRoofMesh.position.set(0, 0.34, 0);
  mailRoofMesh.rotation.y = THREE.Math.degToRad(45);
  mailRoofMesh.receiveShadow = true;
  mailRoofMesh.castShadow = true;
  mailBoxGroup.add(mailRoofMesh);
};

const createMailbox = () => {
  createMailBoxRoof();
  // pole
  createBoxShape(0.05, 0.3, 0.05, 0, 0, 0, colors.brown2, true, true, mailBoxGroup);
  // box
  createBoxShape(0.2, 0.2, 0.2, 0, 0.2, 0, colors.brown2, true, true, mailBoxGroup);
  // hole
  createBoxShape(0.05, 0.05, 0.16, 0.085, 0.22, 0, 0x634326, false, false, mailBoxGroup);
  mainGroup.add(mailBoxGroup);
  mailBoxGroup.position.set(1.5, 0.4, 1.5);
};

// Bunny
let pivot1 = new THREE.Group();
let pivot2 = new THREE.Group();
let pivot3 = new THREE.Group();
let bunnyGroup1 = new THREE.Group();
let bunnyGroup2 = new THREE.Group();
let bunnyGroup3 = new THREE.Group();
const createBunnyShape = (group, x, y, z, color, xPos, yPos, zPos) => {
  let geo = new THREE.BoxBufferGeometry(x, y, z);
  let mat = new THREE.MeshLambertMaterial({ color: color, transparent: true });
  let mesh = new THREE.Mesh(geo, mat);
  mesh.position.set(xPos, yPos, zPos);
  mesh.receiveShadow = true;
  mesh.castShadow = true;
  group.add(mesh);
};

const bunnyColors = [0xFFFFFF, 0xEAEAEA, 0xCFAF8E];
const createBunny = (group, pivotPositionX, pivotPositionY, pivotPositionZ, pivot) => {
  bunnyColor = bunnyColors[Math.round(Math.random() * 2)];
  // head
  createBunnyShape(group, 0.1, 0.1, 0.1, bunnyColor, 0, 0, 0.2);
  // ears
  createBunnyShape(group, 0.025, 0.14, 0.025, bunnyColor, 0.025, 0.05, 0.23);
  createBunnyShape(group, 0.025, 0.14, 0.025, bunnyColor, -0.025, 0.05, 0.23);
  // eyes
  createBunnyShape(group, 0.02, 0.02, 0.02, "black", 0.025, 0.02, 0.25);
  createBunnyShape(group, 0.02, 0.02, 0.02, "black", -0.025, 0.02, 0.25);
  mainGroup.add(group);
  // https://stackoverflow.com/questions/28848863/threejs-how-to-rotate-around-objects-own-center-instead-of-world-center
  let box = new THREE.Box3().setFromObject(group);
  box.getCenter(group.position); // this re-sets the mesh position
  group.position.multiplyScalar(-1);
  pivot.add(group);
  pivot.position.set(pivotPositionX, pivotPositionY, pivotPositionZ);
  mainGroup.add(pivot);

};

// GSAP ANIMATIONS 
// make island floaty
const animateIsland = () => {
  gsap.to(mainGroup.position, { y: '+=0.065', repeat: -1, yoyo: true, ease: "sine.in", duration: 2, yoyoEase: "sine.inOut" });
};
// animate single waterfall particle
const animateDrop = drop => {
  const tl = gsap.timeline({
    onStart: () => {
      gsap.set(drop.position, { y: gsap.utils.random(-0.17, 0, 0.01) });
      gsap.set(drop.scale, { x: 0.1, y: 0.1, z: 0.1 });
    },
    onComplete: animateDrop,
    onCompleteParams: [drop] });


  tl.to(drop.position, {
    y: "-=1",
    ease: "linear",
    delay: gsap.utils.random(0, 2, 0.2),
    duration: 1,
    onStart: () => {
      gsap.to(drop.scale, { x: 0, y: 0, z: 0, delay: 0.14, duration: 0.86 });
    } });

  return tl;
};
// animate single water detail
animateDet = det => {
  const tl = gsap.timeline(
  { defaults: { duration: 1, ease: "sine.in" },
    onStart: () => {
      gsap.set(det.position, { x: gsap.utils.random(0.60, 0.92), z: gsap.utils.random(-1.8, 1.8) });
      gsap.set(det.rotation, { y: 0, z: 0 });
      gsap.set(det.material, { opacity: 0 });
    },
    onComplete: animateDet,
    onCompleteParams: [det] });

  tl.to(det.material, { keyframes: [{ opacity: 0.7 }, { opacity: 0 }] }, 'in').
  to(det.position, { keyframes: [{ z: "+=0.025" }, { z: "-=0.025" }] }, 'in').
  to(det.rotation, { keyframes: [{ y: "-=0.2" }, { z: "+=0.2" }] }, 'in');

  return tl;
};

animateDet2 = det => {
  const tl = gsap.timeline(
  { defaults: { duration: 1, ease: "sine.in" },
    onStart: () => {
      gsap.set(det.position, { x: gsap.utils.random(0.60, 0.92), y: gsap.utils.random(-0.18, 0.20) });
      gsap.set(det.rotation, { y: 0, z: 0 });
      gsap.set(det.material, { opacity: 0 });
    },
    onComplete: animateDet2,
    onCompleteParams: [det] });

  tl.to(det.material, { keyframes: [{ opacity: 0.7 }, { opacity: 0 }] }, 'in').
  to(det.position, { keyframes: [{ y: "-=0.025" }, { y: "+=0.025" }] }, 'in').
  to(det.rotation, { keyframes: [{ y: "-=0.2" }, { z: "+=0.2" }] }, 'in');

  return tl;
};


// animate single puff
const animatePuff = puff => {
  const tl = gsap.timeline({ onComplete: animatePuff, onCompleteParams: [puff], onStart: () => {
      gsap.set(puff.material, { opacity: 1 });
      gsap.set(puff.scale, { x: 0, y: 0, z: 0 });
      gsap.set(puff.position, { y: 1.74 });
    } });
  tl.to(puff.position, { y: '+=0.6', duration: 2, delay: 0.6, ease: "sine.inOut", onStart: () => {
      gsap.to(puff.scale, { keyframes: [{ x: 1, y: 1.4, z: 1 }, { z: 1.4, duration: 0.24 }, { y: 0.8, delay: -0.44, duration: 0.24 }, { x: 1, y: 1 }] });
      gsap.to(puff.material, { opacity: 0, duration: 1.32, delay: 0.68 });
    } });
};
// function to loop over particles
const animateParticles = (fn, array) => {
  for (let i = 0; i < array.length; i++) {
    fn(array[i]);
  }
};

// animate the bunny
// eye blink
const animateBunnyEyes = (group, delay) => {
  const tl = gsap.timeline({ repeat: -1, repeatDelay: 1, defaults: { duration: 0.2 }, delay: delay });
  const eyes = [group.children[3].material, group.children[4].material];
  tl.to(eyes, { keyframes: [{ opacity: 0 }, { opacity: 1 }] }, 'blink');
  return tl;
};

// hop and move
const animateBunny = (pivot, delay) => {
  const tl = gsap.timeline({ repeat: -1, defaults: { duration: 0.32 }, delay: delay });
  tl.to(pivot.position, { keyframes: [{ y: '+=0.1', z: '+=0.1' }, { y: '-=0.1' }, { y: '+=0.1', z: '+=0.1' }, { y: '-=0.1' }] }).
  to(pivot.rotation, { y: 3, duration: 0.6, delay: 0.16 }).
  to(pivot.position, { keyframes: [{ y: '+=0.1', z: '-=0.1' }, { y: '-=0.1' }, { y: '+=0.1', z: '-=0.1' }, { y: '-=0.1' }] }).
  to(pivot.rotation, { y: 0, duration: 0.6, delay: 0.16 });
  return tl;
};

// render
render = () => {

  controls.update();
  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

// resize
const resizeHandler = () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
};

window.addEventListener("resize", () => {
  resizeHandler();
});

window.addEventListener('load', () => {
  setup();
  createIsland();
  animateIsland();
  // waterfall particles creation
  createParticles(
  colors.blue,
  dropCount,
  drops,
  0.1, 0.1, 0.1,
  0.56, 0.95, 0, -0.19, 1.95, 1.95,
  1,
  0, 0, 0);

  // animate waterfall particles 
  animateParticles(animateDrop, drops);
  // water details creation
  createParticles(
  "white",
  detailCount,
  dets,
  0.025, 0.025, 0.025,
  0.60, 0.92, 0.25, 0.26, 1.8, -1.8,
  0,
  0, 0, 0);

  createParticles(
  "white",
  detailCount2,
  dets2,
  0.025, 0.025, 0.025,
  0.60, 0.92, -0.2, 0.23, 2, 2,
  0,
  0, 0, 0);

  // animate water details
  animateParticles(animateDet, dets);
  animateParticles(animateDet2, dets2);
  // trees
  // trees next to house
  createTree(0, 0.75, -0.1, 0, 1.2, -0.1);
  createTree(-1.50, 0.75, -0.1, -1.50, 1.2, -0.1);
  createTree(-0.75, 0.75, -0.5, -0.75, 1.2, -0.5);
  createTree(0, 0.75, -1, 0, 1.2, -1);
  createTree(-1.50, 0.75, -1, -1.50, 1.2, -1);
  createTree(-0.75, 0.75, -1.5, -0.75, 1.2, -1.5);
  // other trees
  createTree(1.5, 0.75, -1.5, 1.5, 1.2, -1.5);
  createTree(1.5, 0.75, -0.5, 1.5, 1.2, -0.5);
  createTree(1.5, 0.75, 0.5, 1.5, 1.2, 0.5);
  // bushes next to house
  createBush(-0.7, 0.28, -0.1);
  createBush(-0.7, 0.28, -1);
  createBush(-1.55, 0.28, -0.6);
  createBush(-1.35, 0.28, -1.5);
  //
  createBush(1.5, 0.28, 1);
  createBush(1.5, 0.28, 0);
  createBush(1.5, 0.28, -1);
  // house
  createHouse();
  // chimney smoke
  createPuffs();
  // animate the smoke
  animateParticles(animatePuff, puffs);
  // mailbox
  createMailbox();
  // bunnies 
  createBunny(bunnyGroup1, 0, 0.33, 0.2, pivot1);
  createBunny(bunnyGroup2, -1, 0.33, -1, pivot2);
  createBunny(bunnyGroup3, -0.2, 0.33, -1.4, pivot3);
  animateBunny(pivot1, 0);
  animateBunny(pivot2, gsap.utils.random(0, 3, 0.4));
  animateBunny(pivot3, gsap.utils.random(0, 3, 0.4));
  animateBunnyEyes(bunnyGroup1, 0);
  animateBunnyEyes(bunnyGroup2, gsap.utils.random(0, 3, 0.4));
  animateBunnyEyes(bunnyGroup3, gsap.utils.random(0, 3, 0.4));
  render();
});

Post a Comment

0Comments
Post a Comment (0)