First commit
This commit is contained in:
		
							
								
								
									
										47
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					<html>
 | 
				
			||||||
 | 
					  <head>
 | 
				
			||||||
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
				
			||||||
 | 
					    <meta name="mobile-web-app-capable" content="yes" />
 | 
				
			||||||
 | 
					    <title>PhotoCalendar</title>
 | 
				
			||||||
 | 
					    <script src="photoCalendar.js"></script>
 | 
				
			||||||
 | 
					    <link rel="stylesheet" type="text/css" href="style.css" />
 | 
				
			||||||
 | 
					  </head>
 | 
				
			||||||
 | 
					  <body>
 | 
				
			||||||
 | 
					    <div class="controls">
 | 
				
			||||||
 | 
					      <div class="control select">
 | 
				
			||||||
 | 
					        <label>Año:</label>
 | 
				
			||||||
 | 
					        <input type="number" min="2024" step="1" value="2024" id="year" />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="row">
 | 
				
			||||||
 | 
					        <div class="col50">
 | 
				
			||||||
 | 
					          <div class="control checkbox">
 | 
				
			||||||
 | 
					            <input type="checkbox" id="showMonthSeparators" />
 | 
				
			||||||
 | 
					            <label for="showMonthSeparators">Dividir meses</label>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="col50">
 | 
				
			||||||
 | 
					          <div class="control checkbox">
 | 
				
			||||||
 | 
					            <input type="checkbox" id="centerMonths" />
 | 
				
			||||||
 | 
					            <label for="centerMonths">Centrar meses</label>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="row">
 | 
				
			||||||
 | 
					        <div class="col50">
 | 
				
			||||||
 | 
					          <div class="control file">
 | 
				
			||||||
 | 
					            <label for="imageInput">Seleccionar Foto</label>
 | 
				
			||||||
 | 
					            <input type="file" id="imageInput" accept="image/*" />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="col50">
 | 
				
			||||||
 | 
					          <div class="control button">
 | 
				
			||||||
 | 
					            <button id="downloadButton">Descargar foto-calendario</button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="preview">
 | 
				
			||||||
 | 
					      <canvas id="calendarCanvas" width="1181" height="1771"></canvas>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										307
									
								
								photoCalendar.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								photoCalendar.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,307 @@
 | 
				
			|||||||
 | 
					class PhotoCalendar {
 | 
				
			||||||
 | 
					  dragOptions = {
 | 
				
			||||||
 | 
					    isDragging: false,
 | 
				
			||||||
 | 
					    lastZoom: 1,
 | 
				
			||||||
 | 
					    MAX_ZOOM: 5,
 | 
				
			||||||
 | 
					    MIN_ZOOM: 0.1,
 | 
				
			||||||
 | 
					    SCROLL_SENSITIVITY: 0.0005,
 | 
				
			||||||
 | 
					    startX: null,
 | 
				
			||||||
 | 
					    startY: null,
 | 
				
			||||||
 | 
					    x: null,
 | 
				
			||||||
 | 
					    y: null,
 | 
				
			||||||
 | 
					    dragging: false,
 | 
				
			||||||
 | 
					    mouseOver: false,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  options = {
 | 
				
			||||||
 | 
					    calendarStartY: 690,
 | 
				
			||||||
 | 
					    divideMonths: false,
 | 
				
			||||||
 | 
					    fillStyle: "white",
 | 
				
			||||||
 | 
					    img: null,
 | 
				
			||||||
 | 
					    photoOffsetX: 0,
 | 
				
			||||||
 | 
					    photoOffsetY: 0,
 | 
				
			||||||
 | 
					    year: 2024,
 | 
				
			||||||
 | 
					    centerMonths: false,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    this.initControls();
 | 
				
			||||||
 | 
					    this.onYearChange();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  overPhotoImg(x, y) {
 | 
				
			||||||
 | 
					    this.mouseOver = y >= 0 && y <= this.options.calendarStartY;
 | 
				
			||||||
 | 
					    return this.mouseOver;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  getEventLocation(e) {
 | 
				
			||||||
 | 
					    if (e.touches && e.touches.length == 1) {
 | 
				
			||||||
 | 
					      return { x: e.touches[0].clientX, y: e.touches[0].clientY };
 | 
				
			||||||
 | 
					    } else if (e.clientX && e.clientY) {
 | 
				
			||||||
 | 
					      return { x: e.clientX, y: e.clientY };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  onPointerDown(e) {
 | 
				
			||||||
 | 
					    this.dragOptions.isDragging = true;
 | 
				
			||||||
 | 
					    let x = this.getEventLocation(e).x;
 | 
				
			||||||
 | 
					    let y = this.getEventLocation(e).y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.overPhotoImg(x, y)) {
 | 
				
			||||||
 | 
					      this.dragOptions.x = x - this.options.photoOffsetX;
 | 
				
			||||||
 | 
					      this.dragOptions.y = y - this.options.photoOffsetY;
 | 
				
			||||||
 | 
					      this.renderPhoto();
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.dragOptions.x = x;
 | 
				
			||||||
 | 
					    this.dragOptions.y = y;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  dragStop() {
 | 
				
			||||||
 | 
					    this.dragOptions.isDragging = false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  onPointerUp(e) {
 | 
				
			||||||
 | 
					    this.dragStop();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onPointerMove(e) {
 | 
				
			||||||
 | 
					    if (this.dragOptions.isDragging) {
 | 
				
			||||||
 | 
					      //      this.options.photoOffsetX = this.getEventLocation(e).x - this.dragOptions.x;
 | 
				
			||||||
 | 
					      this.options.photoOffsetY = this.getEventLocation(e).y - this.dragOptions.y;
 | 
				
			||||||
 | 
					      this.renderPhoto();
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // mouse over
 | 
				
			||||||
 | 
					    let x = this.getEventLocation(e).x;
 | 
				
			||||||
 | 
					    let y = this.getEventLocation(e).y;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  handleTouch(e, singleTouchHandler) {
 | 
				
			||||||
 | 
					    if (e.touches.length <= 1) {
 | 
				
			||||||
 | 
					      singleTouchHandler(e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (e.type == "touchend") {
 | 
				
			||||||
 | 
					        e.preventDefault();
 | 
				
			||||||
 | 
					        this.dragOptions.mouseOver = false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    if (e.type == "touchmove" && e.touches.length == 2)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        this.dragStop();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  initControls() {
 | 
				
			||||||
 | 
					    const self = this;
 | 
				
			||||||
 | 
					    document.getElementById("year").addEventListener("change", (e) => {
 | 
				
			||||||
 | 
					      self.options.year = e.target.value;
 | 
				
			||||||
 | 
					      self.onYearChange();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.getElementById("showMonthSeparators").addEventListener("click", (e) => {
 | 
				
			||||||
 | 
					      self.options.divideMonths = e.target.checked;
 | 
				
			||||||
 | 
					      self.onYearChange();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.getElementById("centerMonths").addEventListener("click", (e) => {
 | 
				
			||||||
 | 
					      self.options.centerMonths = e.target.checked;
 | 
				
			||||||
 | 
					      self.onYearChange();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.getElementById("imageInput").addEventListener("change", (e) => self.handleImageUpload(e));
 | 
				
			||||||
 | 
					    document.getElementById("downloadButton").addEventListener("click", (e) => self.downloadCanvas());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const canvas = this.getCanvas();
 | 
				
			||||||
 | 
					    canvas.addEventListener("mousedown", (e) => self.onPointerDown(e));
 | 
				
			||||||
 | 
					    canvas.addEventListener("touchstart", (e) => self.handleTouch(e, (e) => self.onPointerDown(e)));
 | 
				
			||||||
 | 
					    canvas.addEventListener("mouseup", (e) => self.onPointerUp(e));
 | 
				
			||||||
 | 
					    canvas.addEventListener("touchend", (e) => self.handleTouch(e, (e) => self.onPointerUp(e)));
 | 
				
			||||||
 | 
					    canvas.addEventListener("mousemove", (e) => self.onPointerMove(e));
 | 
				
			||||||
 | 
					    canvas.addEventListener("touchmove", (e) => self.handleTouch(e, (e) => self.onPointerMove(e)));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleImageUpload(event) {
 | 
				
			||||||
 | 
					    const self = this;
 | 
				
			||||||
 | 
					    const canvas = self.getCanvas();
 | 
				
			||||||
 | 
					    const ctx = canvas.getContext("2d");
 | 
				
			||||||
 | 
					    const file = event.target.files[0];
 | 
				
			||||||
 | 
					    const reader = new FileReader();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    reader.onload = function (e) {
 | 
				
			||||||
 | 
					      const img = new Image();
 | 
				
			||||||
 | 
					      img.onload = function () {
 | 
				
			||||||
 | 
					        self.options.img = img;
 | 
				
			||||||
 | 
					        self.renderPhoto();
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      img.src = e.target.result;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    reader.readAsDataURL(file);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  onYearChange() {
 | 
				
			||||||
 | 
					    const calendar = this.createCalendar(this.options.year);
 | 
				
			||||||
 | 
					    this.renderCalendar(calendar);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createCalendar(year) {
 | 
				
			||||||
 | 
					    const calendar = [];
 | 
				
			||||||
 | 
					    const daysInWeek = 7;
 | 
				
			||||||
 | 
					    const monthsInYear = 12;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Helper function to determine the number of days in a given month and year
 | 
				
			||||||
 | 
					    function getDaysInMonth(month, year) {
 | 
				
			||||||
 | 
					      return new Date(year, month + 1, 0).getDate();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Helper function to get the day of the week (0 is Monday, 6 is Sunday)
 | 
				
			||||||
 | 
					    function getDayOfWeek(year, month, day) {
 | 
				
			||||||
 | 
					      const date = new Date(year, month, day);
 | 
				
			||||||
 | 
					      const dayOfWeek = date.getDay();
 | 
				
			||||||
 | 
					      return (dayOfWeek + 6) % 7; // Adjust so Monday is 0 and Sunday is 6
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (let month = 0; month < monthsInYear; month++) {
 | 
				
			||||||
 | 
					      const daysInMonth = getDaysInMonth(month, year);
 | 
				
			||||||
 | 
					      const monthArray = [];
 | 
				
			||||||
 | 
					      let weekArray = Array(daysInWeek).fill(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (let day = 1; day <= daysInMonth; day++) {
 | 
				
			||||||
 | 
					        const dayOfWeek = getDayOfWeek(year, month, day);
 | 
				
			||||||
 | 
					        weekArray[dayOfWeek] = day;
 | 
				
			||||||
 | 
					        if (dayOfWeek === 6 || day === daysInMonth) {
 | 
				
			||||||
 | 
					          // End of the week or month
 | 
				
			||||||
 | 
					          monthArray.push(weekArray);
 | 
				
			||||||
 | 
					          weekArray = Array(daysInWeek).fill(null);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      calendar.push(monthArray);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return calendar;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getCanvas() {
 | 
				
			||||||
 | 
					    const canvas = document.getElementById("calendarCanvas");
 | 
				
			||||||
 | 
					    return canvas;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  renderPhoto() {
 | 
				
			||||||
 | 
					    const canvas = this.getCanvas();
 | 
				
			||||||
 | 
					    const ctx = canvas.getContext("2d");
 | 
				
			||||||
 | 
					    ctx.fillStyle = this.options.fillStyle;
 | 
				
			||||||
 | 
					    ctx.fillRect(0, 0, canvas.width, this.options.calendarStartY - 1); // Clear canvas
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.options.img == null) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Calculate the aspect ratio and new height
 | 
				
			||||||
 | 
					    const aspectRatio = this.options.img.width / this.options.img.height;
 | 
				
			||||||
 | 
					    const newWidth = canvas.width;
 | 
				
			||||||
 | 
					    const newHeight = newWidth / aspectRatio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx.save();
 | 
				
			||||||
 | 
					    ctx.beginPath();
 | 
				
			||||||
 | 
					    ctx.rect(0, 0, canvas.width, this.options.calendarStartY - 1);
 | 
				
			||||||
 | 
					    ctx.clip();
 | 
				
			||||||
 | 
					    ctx.drawImage(this.options.img, this.options.photoOffsetX, this.options.photoOffsetY, newWidth, newHeight);
 | 
				
			||||||
 | 
					    ctx.restore();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  downloadCanvas() {
 | 
				
			||||||
 | 
					    const canvas = this.getCanvas();
 | 
				
			||||||
 | 
					    const link = document.createElement("a");
 | 
				
			||||||
 | 
					    link.href = canvas.toDataURL("image/png");
 | 
				
			||||||
 | 
					    link.download = "calendar.png";
 | 
				
			||||||
 | 
					    link.click();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  renderCalendar(calendar) {
 | 
				
			||||||
 | 
					    const canvas = this.getCanvas();
 | 
				
			||||||
 | 
					    const ctx = canvas.getContext("2d");
 | 
				
			||||||
 | 
					    ctx.fillStyle = this.options.fillStyle;
 | 
				
			||||||
 | 
					    ctx.fillRect(0, 0, canvas.width, canvas.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.renderPhoto();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx.fillStyle = this.options.fillStyle;
 | 
				
			||||||
 | 
					    ctx.fillRect(0, this.options.calendarStartY, canvas.width, canvas.height);
 | 
				
			||||||
 | 
					    const monthWidth = canvas.width / 3;
 | 
				
			||||||
 | 
					    const monthHeigth = 30 + 30 * 8;
 | 
				
			||||||
 | 
					    for (let i = 0; i < 12; i++) {
 | 
				
			||||||
 | 
					      this.renderMonth(calendar, i, canvas, (i % 3) * monthWidth, parseInt(i / 3) * monthHeigth + this.options.calendarStartY, monthWidth);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  drawText(ctx, text, x, y) {
 | 
				
			||||||
 | 
					    ctx.textAlign = "left";
 | 
				
			||||||
 | 
					    ctx.textBaseline = "middle";
 | 
				
			||||||
 | 
					    ctx.fillText(text, x, y);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  drawTextCentered(ctx, text, x, y, cellWidth) {
 | 
				
			||||||
 | 
					    const centerX = x + cellWidth / 2;
 | 
				
			||||||
 | 
					    ctx.textAlign = "center";
 | 
				
			||||||
 | 
					    ctx.textBaseline = "middle";
 | 
				
			||||||
 | 
					    ctx.fillText(text, centerX, y);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  renderMonth(calendar, month, canvas, x, y, w) {
 | 
				
			||||||
 | 
					    const ctx = canvas.getContext("2d");
 | 
				
			||||||
 | 
					    const monthNames = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"];
 | 
				
			||||||
 | 
					    const dayInitials = ["L", "M", "X", "J", "V", "S", "D"];
 | 
				
			||||||
 | 
					    const cellWidth = w / 8;
 | 
				
			||||||
 | 
					    const cellHeight = 30;
 | 
				
			||||||
 | 
					    const headerHeight = 30;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.options.divideMonths) {
 | 
				
			||||||
 | 
					      ctx.beginPath();
 | 
				
			||||||
 | 
					      ctx.rect(x, y, w, 30 + 30 * 8);
 | 
				
			||||||
 | 
					      ctx.stroke();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    x += cellWidth / 2;
 | 
				
			||||||
 | 
					    y += headerHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Clear the area where the month will be rendered
 | 
				
			||||||
 | 
					    ctx.fillStyle = this.options.fillStyle;
 | 
				
			||||||
 | 
					    ctx.fillRect(x, y, 7 * cellWidth, calendar[month].length * cellHeight + headerHeight * 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Draw the month name
 | 
				
			||||||
 | 
					    ctx.fillStyle = "black";
 | 
				
			||||||
 | 
					    ctx.font = "bold 28px Arial";
 | 
				
			||||||
 | 
					    if (this.options.centerMonths) {
 | 
				
			||||||
 | 
					      this.drawTextCentered(ctx, monthNames[month], x, y, cellWidth * 7);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.drawText(ctx, monthNames[month], x + cellWidth / 3, y);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Draw the day initials header
 | 
				
			||||||
 | 
					    ctx.font = "24px Arial";
 | 
				
			||||||
 | 
					    for (let i = 0; i < dayInitials.length; i++) {
 | 
				
			||||||
 | 
					      // ctx.fillText(dayInitials[i], x + i * cellWidth, y + headerHeight);
 | 
				
			||||||
 | 
					      this.drawTextCentered(ctx, dayInitials[i], x + i * cellWidth, y + headerHeight, cellWidth);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Draw the days of the month
 | 
				
			||||||
 | 
					    ctx.font = "22px Arial";
 | 
				
			||||||
 | 
					    const monthData = calendar[month];
 | 
				
			||||||
 | 
					    for (let week = 0; week < monthData.length; week++) {
 | 
				
			||||||
 | 
					      for (let day = 0; day < monthData[week].length; day++) {
 | 
				
			||||||
 | 
					        const dayNumber = monthData[week][day];
 | 
				
			||||||
 | 
					        const dayText = dayNumber !== null ? dayNumber : "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Set color to red for Saturday (5) and Sunday (6), else default color
 | 
				
			||||||
 | 
					        if (day === 5 || day === 6) {
 | 
				
			||||||
 | 
					          ctx.fillStyle = "red";
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          ctx.fillStyle = "black";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // ctx.fillText(dayText, x + day * cellWidth, y + headerHeight * 2 + week * cellHeight);
 | 
				
			||||||
 | 
					        this.drawTextCentered(ctx, dayText, x + day * cellWidth, y + headerHeight * 2 + week * cellHeight, cellWidth);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					document.addEventListener("DOMContentLoaded", () => {
 | 
				
			||||||
 | 
					  const app = new PhotoCalendar();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										71
									
								
								style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								style.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					body {
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
 | 
				
			||||||
 | 
					  font-size: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.control {
 | 
				
			||||||
 | 
					  padding-bottom: 0.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.preview canvas {
 | 
				
			||||||
 | 
					  border: 1px solid black;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.control.file input[type="file"] {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.control.file label {
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  border: 1px solid black;
 | 
				
			||||||
 | 
					  -display: block;
 | 
				
			||||||
 | 
					  padding: 0.25rem;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.control.button button {
 | 
				
			||||||
 | 
					  border-radius: 4px;
 | 
				
			||||||
 | 
					  border: 1px solid black;
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  padding: 0.25rem;
 | 
				
			||||||
 | 
					  font-size: 1rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (min-aspect-ratio: 1/1) {
 | 
				
			||||||
 | 
					  /* Horizontal */
 | 
				
			||||||
 | 
					  .preview canvas {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    top: 0;
 | 
				
			||||||
 | 
					    right: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .row {
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .col50 {
 | 
				
			||||||
 | 
					    width: 50%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media (max-aspect-ratio: 1/1) {
 | 
				
			||||||
 | 
					  /* Vertical */
 | 
				
			||||||
 | 
					  .preview canvas {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    bottom: 0;
 | 
				
			||||||
 | 
					    left: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .control.file label {
 | 
				
			||||||
 | 
					    width: 90%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .control.button button {
 | 
				
			||||||
 | 
					    width: 90%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .row {
 | 
				
			||||||
 | 
					    width: 100%;
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  .col50 {
 | 
				
			||||||
 | 
					    width: 50%;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user