DeviceM.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { DeviceType } from "cc";
  2. export default class DeviceM {
  3. private static _ins: DeviceM;
  4. public static get ins(): DeviceM {
  5. return (DeviceM._ins ??= new DeviceM());
  6. }
  7. public async isMobile(): Promise<boolean> {
  8. // 檢查是否在瀏覽器環境中
  9. if (typeof window === "undefined") {
  10. return false;
  11. }
  12. // 方法1: 檢查 User Agent
  13. const userAgent = navigator.userAgent.toLowerCase();
  14. const mobileKeywords = [
  15. "android",
  16. "iphone",
  17. "ipad",
  18. "ipod",
  19. "blackberry",
  20. "windows phone",
  21. "mobile",
  22. "tablet",
  23. "opera mini",
  24. "opera mobi",
  25. ];
  26. const isMobileByUserAgent = mobileKeywords.some((keyword) =>
  27. userAgent.includes(keyword)
  28. );
  29. // 方法2: 檢查螢幕尺寸
  30. const isMobileByScreen =
  31. window.innerWidth <= 768 || window.innerHeight <= 768;
  32. // 方法3: 檢查觸控支援
  33. const isMobileByTouch =
  34. "ontouchstart" in window || navigator.maxTouchPoints > 0;
  35. // 方法4: 檢查設備像素比
  36. const isMobileByPixelRatio = window.devicePixelRatio > 1;
  37. // 綜合判斷:如果任一條件成立,則認為是移動端
  38. return (
  39. isMobileByUserAgent ||
  40. (isMobileByScreen && isMobileByTouch) ||
  41. isMobileByPixelRatio
  42. );
  43. }
  44. /**
  45. * 檢查瀏覽器是否支持打開新標籤頁面
  46. * @returns Promise<boolean>
  47. */
  48. public async supportsNewTab(): Promise<boolean> {
  49. // 檢查是否在瀏覽器環境中
  50. if (typeof window === "undefined") {
  51. return false;
  52. }
  53. // 檢查是否在 iframe 中(iframe 通常無法打開新標籤頁)
  54. if (window.self !== window.top) {
  55. return false;
  56. }
  57. // 檢查是否在彈出視窗中(彈出視窗通常無法打開新標籤頁)
  58. if (window.opener) {
  59. return false;
  60. }
  61. // 檢查是否在 WebView 中(某些 WebView 可能不支持新標籤頁)
  62. const userAgent = navigator.userAgent.toLowerCase();
  63. const webViewKeywords = [
  64. "webview",
  65. "wv",
  66. "mobile safari",
  67. "chrome mobile",
  68. "firefox mobile",
  69. "opera mobile",
  70. ];
  71. const isInWebView = webViewKeywords.some((keyword) =>
  72. userAgent.includes(keyword)
  73. );
  74. // 檢查是否在無頭瀏覽器中(如 Puppeteer、Selenium 等)
  75. const isHeadless =
  76. userAgent.includes("headless") ||
  77. userAgent.includes("phantomjs") ||
  78. userAgent.includes("selenium");
  79. // 檢查是否支持 window.open 方法
  80. const supportsWindowOpen = typeof window.open === "function";
  81. // 綜合判斷:支持新標籤頁的條件
  82. return supportsWindowOpen && !isInWebView && !isHeadless;
  83. }
  84. /**
  85. * 嘗試打開新標籤頁
  86. * @param url 要打開的 URL
  87. * @param target 目標視窗(默認為 '_blank')
  88. * @returns Promise<boolean> 是否成功打開
  89. */
  90. public async openNewTab(
  91. url: string,
  92. target: string = "_blank"
  93. ): Promise<boolean> {
  94. try {
  95. // 首先檢查是否支持新標籤頁
  96. const canOpen = await this.supportsNewTab();
  97. if (!canOpen) {
  98. console.warn("當前環境不支持打開新標籤頁");
  99. return false;
  100. }
  101. // 嘗試打開新標籤頁
  102. const newWindow = window.open(url, target);
  103. // 檢查是否成功打開
  104. if (newWindow && !newWindow.closed) {
  105. return true;
  106. } else {
  107. console.warn("無法打開新標籤頁,可能被瀏覽器阻擋");
  108. return false;
  109. }
  110. } catch (error) {
  111. console.error("打開新標籤頁時發生錯誤:", error);
  112. return false;
  113. }
  114. }
  115. }