adList.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /**
  2. * 创建广告
  3. * config 参数说明
  4. * @param {string[]} ad_id 广告id
  5. * @param {string} ad_type 广告类型 1:视频 2:图片
  6. * @param {string} ad_pos 广告位置
  7. * @param {string[]} ad_size 广告尺寸
  8. * @param {string} ad_text 广告文案
  9. * @param {string[]} ad_url 广告链接
  10. * @param {string[]} ad_cover 广告图片
  11. */
  12. document.addEventListener('DOMContentLoaded', function () {
  13. const base = 'http://10.30.160.129:8082/'
  14. /**
  15. * 广告初始化
  16. * @param config 广告配置
  17. * @returns
  18. */
  19. function ad_init(config = {}) {
  20. // 启动上报
  21. const slots = config.slots || []
  22. const D = new Date()
  23. const dateTime = D.getTime()
  24. for (let i = 0; i < slots.length; i++) {
  25. const v = slots[i]
  26. if (!v.creativesList || !v.creativesList.length) continue
  27. // 获取对应广告位置
  28. const ad_pos = document.querySelector('#sxtv-ad-' + v.slotId)
  29. if (!ad_pos) {
  30. console.error('广告位置不存在:#sxtv-ad-' + v.slotId)
  31. continue
  32. }
  33. const gg = document.createElement('div')
  34. gg.innerHTML =
  35. '<svg t="1734493782806" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9775" width="32" height="32"><path d="M560.88 658.8h69.68v15.44h-69.68z" p-id="9776" fill="#515151"></path><path d="M698.24 451.44V200h-46.56v251.44H372.32V200h-46.56v251.44H186V824h651.92V451.44zM505.12 600H396.16v90.72L352 700.08v-33.92l8-2.4V575.2h54.24l-1.84-8.8h45.84l1.68 8.8h44.88z m162.72 96.48H524.4v-60h143.44z m5.36-64.48H518.88v-23.36h61.12v-11.28h-22.16l-2.88 8.48H520V582.4h8.72l5.52-13.76H568l-2.32 6.64h14.32v-8h38.64v8h49.04v22h-49.04v11.36h54.56z" p-id="9777" fill="#515151"></path></svg>'
  36. gg.style.position = 'absolute'
  37. gg.style.top = 0
  38. gg.style.right = 3
  39. gg.style.zIndex = 999999
  40. // 判定当前时间是否在creativesList的时间内
  41. const ele = document.createElement('div')
  42. ele.style.position = 'relative'
  43. ele.appendChild(gg)
  44. for (let o = 0; o < v.creativesList.length; o++) {
  45. const item = v.creativesList[o]
  46. const start = new Date(item.startDate + ' 00:00:00').getTime()
  47. const end = new Date(item.endDate + '').getTime()
  48. // 判断变量是否数组
  49. if (dateTime < start || dateTime > end || !Array.isArray(item.stuffsList)) continue
  50. // 获取广告类型 1 轮播 2 交替 3 单项
  51. const showType = item.showType
  52. let e =
  53. showType === 1
  54. ? generateCarouselAd(item, D, v.slotId, v.width || 0, v.height || 0)
  55. : generateAlternateAd(item, D, v.slotId)
  56. if (e == -1) return
  57. ele.appendChild(e)
  58. ele.style.width = '100%'
  59. ele.style.height = '100%'
  60. }
  61. ad_pos.style.width = (v.width || 0) + 'px'
  62. ad_pos.style.height = (v.height || 0) + 'px'
  63. ad_pos.style.border = 'none'
  64. ad_pos.style.overflow = 'hidden'
  65. ad_pos.appendChild(ele)
  66. }
  67. }
  68. // 生成轮播广告
  69. const generateCarouselAd = function (generateCarousel = {}, D, slotId, width, height) {
  70. const week = D.getDay() === 0 ? 7 : D.getDay()
  71. const timeInterval = (generateCarousel.timeInterval || '')
  72. .slice(week * 24, week * 24 + 25)
  73. .split('')
  74. const H = D.getHours()
  75. if (timeInterval[H] == 0) return -1
  76. const T = (generateCarousel.showIntervalTime || 5) * 1000
  77. return createCarousel(generateCarousel.stuffsList, T, slotId, width, height)
  78. }
  79. // 生成交替广告
  80. const generateAlternateAd = function (generateAlternate = {}, D, slotId) {
  81. const week = D.getDay() === 0 ? 7 : D.getDay()
  82. const timeInterval = (generateAlternate.timeInterval || '')
  83. .slice(week * 24, week * 24 + 24)
  84. .split('')
  85. const H = D.getHours()
  86. if (timeInterval[H] == 0) return -1
  87. const stuff =
  88. generateAlternate.stuffsList[
  89. Math.floor((H * 60 + D.getMinutes()) / (generateAlternate.showIntervalTime || 5)) %
  90. generateAlternate.stuffsList.length
  91. ]
  92. if (!stuff) return -1
  93. const fileType = stuff.addr.split('.').pop()
  94. // fileType是否是图片类型
  95. if (fileType === 'jpg' || fileType === 'png' || fileType === 'gif') {
  96. const son_ele = document.createElement('img')
  97. son_ele.src = stuff.addr
  98. son_ele.style.width = '100%'
  99. son_ele.style.height = '100%'
  100. son_ele.onload = () => {
  101. const uuid = localStorage.getItem('ad_id')
  102. fetch(`${base}ad/show?uuid=${uuid}&stuffId=${stuff.stuffId}&slotId=${slotId}`).then((res) =>
  103. res.text(),
  104. )
  105. }
  106. son_ele.addEventListener('click', () => {
  107. const uuid = localStorage.getItem('ad_id')
  108. // 点击广告
  109. fetch(`${base}ad/click?uuid=${uuid}&stuffId=${stuff.stuffId}&slotId=${slotId}`)
  110. .then((res) => res.text())
  111. .then(() => {
  112. window.open(stuff.landingPage)
  113. })
  114. })
  115. return son_ele
  116. }
  117. const son_ele = document.createElement('video')
  118. son_ele.setAttribute('loop', 'loop')
  119. son_ele.src = stuff.addr
  120. son_ele.style.width = '100%'
  121. son_ele.style.height = '100%'
  122. // 视频加载允许播放的回调
  123. son_ele.oncanplay = () => {
  124. son_ele.play()
  125. const uuid = localStorage.getItem('ad_id')
  126. fetch(`${base}ad/show?uuid=${uuid}&stuffId=${src.stuffId}&slotId=${slotId}`).then((res) =>
  127. res.text(),
  128. )
  129. }
  130. son_ele.addEventListener('click', () => {
  131. const uuid = localStorage.getItem('ad_id')
  132. // 点击广告
  133. fetch(`${base}ad/click?uuid=${uuid}&stuffId=${stuff.stuffId}&slotId=${slotId}`)
  134. .then((res) => res.text())
  135. .then(() => {
  136. window.open(stuff.landingPage)
  137. })
  138. })
  139. // 对body添加一次性点击事件
  140. const play = () => {
  141. son_ele.play()
  142. // 移除点击
  143. document.body.removeEventListener('click', play)
  144. }
  145. document.body.addEventListener('click', play)
  146. return son_ele
  147. }
  148. function generateBrowserFingerprint() {
  149. return new Promise((resolve, reject) => {
  150. // 在这里执行获取指纹的操作,例如使用Canvas、WebGL等方法
  151. // 然后将生成的指纹字符串作为参数传递给resolve函数
  152. // 获取浏览器信息
  153. const browserInfo = `${navigator.userAgent} ${window.screen.width}x${window.screen.height}`
  154. // 获取时区偏移量
  155. const timezoneOffset = Intl.DateTimeFormat().resolvedOptions().timeZone
  156. // 在这里处理获取到的指纹信息
  157. const canvasFingerprint = getCanvasFingerprint()
  158. const webglFingerprint = getWebglFingerprint()
  159. // 将所有信息组合成一个字符串
  160. const fingerprintStr = `${browserInfo}|${timezoneOffset}|${canvasFingerprint}|${webglFingerprint}`
  161. // 使用createHash生成哈希值
  162. createHash(fingerprintStr)
  163. .then((hash) => {
  164. resolve(hash)
  165. })
  166. .catch((error) => {
  167. reject(error)
  168. })
  169. })
  170. }
  171. // Canvas指纹获取函数
  172. function getCanvasFingerprint() {
  173. const canvas = document.createElement('canvas')
  174. const ctx = canvas.getContext('2d')
  175. if (!ctx) return ''
  176. // 绘制一些内容
  177. ctx.fillStyle = '#f60'
  178. ctx.fillRect(0, 0, 16, 16)
  179. ctx.fillStyle = '#069'
  180. ctx.font = '11pt Arial'
  181. ctx.fillText('Cwm fjord!!', 4, 10)
  182. ctx.fillStyle = 'rgba(102, 204, 0, 0.7)'
  183. ctx.fillText('Cwm fjord!!', 5, 11)
  184. ctx.scale(2, 2)
  185. ctx.fillStyle = '#fff'
  186. ctx.font = '24pt Arial'
  187. ctx.fillText('Cwm fjord!!', 10, 25)
  188. ctx.fillStyle = 'rgba(102, 204, 0, 0.7)'
  189. ctx.fillText('Cwm fjord!!', 10, 25)
  190. const data = canvas.toDataURL()
  191. return data.replace(/data:image\/png;base64,/, '')
  192. }
  193. // WebGL指纹获取函数
  194. function getWebglFingerprint() {
  195. const canvas = document.createElement('canvas')
  196. const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
  197. if (!gl) return ''
  198. const extensions = gl.getSupportedExtensions()
  199. if (!extensions) return ''
  200. const extStr = extensions.join(',')
  201. const rendererInfo = gl.getParameter(gl.RENDERER) + '/' + gl.getParameter(gl.VENDOR)
  202. return rendererInfo + extStr
  203. }
  204. function createHash(str) {
  205. if (!crypto || !crypto.subtle || !crypto.subtle.digest || !TextEncoder) {
  206. console.error('浏览器不支持创建哈希值')
  207. return Promise.reject()
  208. }
  209. // 将字符串转换为 Uint8Array
  210. const encoder = new TextEncoder()
  211. const data = encoder.encode(str)
  212. // 使用 SubtleCrypto API 计算 SHA-256 摘要
  213. return new Promise((resolve, reject) => {
  214. crypto.subtle
  215. .digest('SHA-256', data)
  216. .then((hashBuffer) => {
  217. // 将 ArrayBuffer 转换为十六进制字符串
  218. const hashArray = Array.from(new Uint8Array(hashBuffer))
  219. const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('')
  220. resolve(hashHex)
  221. })
  222. .catch((error) => {
  223. reject(error)
  224. })
  225. })
  226. }
  227. // 轮播
  228. function createCarousel(images, interval, slotId, width, height) {
  229. // 创建轮播图容器
  230. const carouselContainer = document.createElement('div')
  231. carouselContainer.style = `
  232. position: relative;
  233. width: 100%;
  234. height: 100%;
  235. margin: auto;
  236. overflow: hidden;
  237. `
  238. // 创建图片容器
  239. const imagesContainer = document.createElement('div')
  240. imagesContainer.style = `
  241. display: flex;
  242. width: ${images.length * width}px;
  243. height: ${height}px;
  244. transition: transform 0.5s ease;
  245. `
  246. // 创建指示器容器
  247. const indicatorsContainer = document.createElement('div')
  248. indicatorsContainer.style = `
  249. position: absolute;
  250. bottom: 10px;
  251. left: 50%;
  252. transform: translateX(-50%);
  253. display: flex;
  254. gap: 5px;
  255. `
  256. // 添加图片到容器
  257. images.forEach((src, index) => {
  258. const img = document.createElement('img')
  259. img.src = src.addr
  260. img.alt = `Image ${index + 1}`
  261. img.style = `
  262. width: 100%;
  263. flex-shrink: 0;
  264. height: 100%;
  265. display: block;
  266. `
  267. if (index !== 0) img.style.display = 'none'
  268. img.addEventListener('click', () => {
  269. const uuid = localStorage.getItem('ad_id')
  270. // 点击广告
  271. fetch(`${base}ad/click?uuid=${uuid}&stuffId=${src.stuffId}&slotId=${slotId}`)
  272. .then((res) => res.text())
  273. .then(() => {
  274. window.open(v.ad_url)
  275. })
  276. })
  277. // 图片加载完成
  278. img.onload = () => {
  279. const uuid = localStorage.getItem('ad_id')
  280. fetch(`${base}ad/show?uuid=${uuid}&stuffId=${src.stuffId}&slotId=${slotId}`).then((res) =>
  281. res.text(),
  282. )
  283. }
  284. imagesContainer.appendChild(img)
  285. // 添加指示器
  286. const indicator = document.createElement('button')
  287. indicator.style = `
  288. border: none;
  289. background-color: #fff;
  290. cursor: pointer;
  291. padding: 5px;
  292. border-radius: 50%;
  293. width: 10px;
  294. height: 10px;
  295. display: inline-block;
  296. `
  297. if (index !== 0) indicator.style.backgroundColor = '#ccc'
  298. indicator.dataset.target = index
  299. indicatorsContainer.appendChild(indicator)
  300. })
  301. // 将图片容器和指示器容器添加到轮播图容器
  302. carouselContainer.appendChild(imagesContainer)
  303. carouselContainer.appendChild(indicatorsContainer)
  304. // 设置轮播图的切换逻辑
  305. let currentImageIndex = 0
  306. const maxImages = images.length
  307. function showImage(index) {
  308. // 更新图片位置
  309. imagesContainer.style.transform = `translateX(-${index * 100}%)`
  310. // 更新指示器
  311. indicatorsContainer.querySelectorAll('button').forEach((btn, i) => {
  312. btn.style.backgroundColor = i === index ? '#000' : '#ccc'
  313. })
  314. currentImageIndex = index
  315. }
  316. function nextImage() {
  317. currentImageIndex = (currentImageIndex + 1) % maxImages
  318. showImage(currentImageIndex)
  319. }
  320. // 为指示器添加点击事件
  321. indicatorsContainer.addEventListener('click', function (e) {
  322. if (e.target.tagName === 'BUTTON') {
  323. const targetIndex = parseInt(e.target.dataset.target)
  324. showImage(targetIndex)
  325. }
  326. })
  327. // 自动播放轮播图
  328. let intervalId = setInterval(nextImage, interval)
  329. // 可选:鼠标悬停时停止自动播放
  330. carouselContainer.addEventListener('mouseenter', () => {
  331. clearInterval(intervalId)
  332. })
  333. carouselContainer.addEventListener('mouseleave', () => {
  334. intervalId = setInterval(nextImage, interval)
  335. })
  336. return carouselContainer
  337. }
  338. // 轮播End
  339. const CFG = '__AD_CONFIG_URL__'
  340. try {
  341. fetch(CFG + '?' + Date.now())
  342. .then((res) => res.json())
  343. .then((res) => {
  344. const data = res
  345. // 广告sdk
  346. if (localStorage.getItem('ad_id')) {
  347. ad_init(data)
  348. return
  349. }
  350. // 生成并输出浏览器指纹
  351. generateBrowserFingerprint().then((ad_id) => {
  352. localStorage.setItem('ad_id', ad_id + '')
  353. ad_init(data)
  354. })
  355. })
  356. } catch (error) {
  357. console.error(error)
  358. }
  359. })